diff options
author | rockot <rockot@chromium.org> | 2015-11-12 17:33:59 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-11-13 01:34:47 +0000 |
commit | 85dce086001825a2faa4e75755a669f5e08a1cad (patch) | |
tree | 722de1d974f799b3d1ee1ca4c81bb8b0fa75a95d /mojo | |
parent | 415b73b1a400a994a86e6f29709aa0271e895dd5 (diff) | |
download | chromium_src-85dce086001825a2faa4e75755a669f5e08a1cad.zip chromium_src-85dce086001825a2faa4e75755a669f5e08a1cad.tar.gz chromium_src-85dce086001825a2faa4e75755a669f5e08a1cad.tar.bz2 |
Move third_party/mojo/src/mojo/public to mojo/public
BUG=None
NOPRESUBMIT=true
Review URL: https://codereview.chromium.org/1410053006
Cr-Commit-Position: refs/heads/master@{#359461}
Diffstat (limited to 'mojo')
831 files changed, 67320 insertions, 505 deletions
diff --git a/mojo/BUILD.gn b/mojo/BUILD.gn index da4601d..13970fa 100644 --- a/mojo/BUILD.gn +++ b/mojo/BUILD.gn @@ -17,7 +17,7 @@ group("mojo") { # TODO(GYP): Figure out if this needs to be supported. Right now # it won't work on x86 official builds because it needs stuff in the # sysroot that doesn't exist. - deps += [ "//third_party/mojo/src/mojo/public" ] + deps += [ "//mojo/public" ] } if (is_android) { @@ -4,5 +4,4 @@ include_rules = [ "+testing", "+third_party/mojo/src/mojo/edk", "-third_party/mojo/src/mojo/edk/system", - "+third_party/mojo/src/mojo/public", ] diff --git a/mojo/android/BUILD.gn b/mojo/android/BUILD.gn index d25b7d5e..d1dce6b 100644 --- a/mojo/android/BUILD.gn +++ b/mojo/android/BUILD.gn @@ -47,8 +47,8 @@ source_set("libsystem_java") { "//base", "//mojo/environment:chromium", "//mojo/message_pump", + "//mojo/public/cpp/environment", "//third_party/mojo/src/mojo/edk/system", - "//third_party/mojo/src/mojo/public/cpp/environment", ] } @@ -66,7 +66,7 @@ android_library("system_java") { deps = [ "//base:base_java", - "//third_party/mojo/src/mojo/public/java:system", + "//mojo/public/java:system", ] } @@ -102,9 +102,9 @@ android_library("mojo_javatests") { ":system_java", "//base:base_java", "//base:base_java_test_support", - "//third_party/mojo/src/mojo/public/interfaces/bindings/tests:test_interfaces_java", - "//third_party/mojo/src/mojo/public/java:bindings", - "//third_party/mojo/src/mojo/public/java:system", + "//mojo/public/interfaces/bindings/tests:test_interfaces_java", + "//mojo/public/java:bindings", + "//mojo/public/java:system", ] } @@ -127,10 +127,10 @@ shared_library("mojo_java_unittests") { "//base/test/:test_support", "//build/config/sanitizers:deps", "//mojo/message_pump", + "//mojo/public/cpp/bindings/tests:mojo_public_bindings_test_utils", + "//mojo/public/cpp/test_support:test_utils", + "//mojo/public/cpp/environment", "//third_party/mojo/src/mojo/edk/system", - "//third_party/mojo/src/mojo/public/cpp/bindings/tests:mojo_public_bindings_test_utils", - "//third_party/mojo/src/mojo/public/cpp/test_support:test_utils", - "//third_party/mojo/src/mojo/public/cpp/environment", ] defines = [ "UNIT_TEST" ] } @@ -142,8 +142,8 @@ android_apk("mojo_test_apk") { ":mojo_java_unittests", ":system_java", "//base:base_java", - "//third_party/mojo/src/mojo/public/java:bindings", - "//third_party/mojo/src/mojo/public/interfaces/bindings/tests:test_interfaces", + "//mojo/public/java:bindings", + "//mojo/public/interfaces/bindings/tests:test_interfaces", ] native_libs = [ "libmojo_java_unittests.so" ] apk_name = "MojoTest" diff --git a/mojo/android/javatests/mojo_test_case.cc b/mojo/android/javatests/mojo_test_case.cc index 9170fd0..22a08ff 100644 --- a/mojo/android/javatests/mojo_test_case.cc +++ b/mojo/android/javatests/mojo_test_case.cc @@ -14,7 +14,7 @@ #include "base/test/test_support_android.h" #include "jni/MojoTestCase_jni.h" #include "mojo/message_pump/message_pump_mojo.h" -#include "third_party/mojo/src/mojo/public/cpp/environment/environment.h" +#include "mojo/public/cpp/environment/environment.h" namespace { diff --git a/mojo/android/javatests/validation_test_util.cc b/mojo/android/javatests/validation_test_util.cc index 8179fd6..1fb0abf 100644 --- a/mojo/android/javatests/validation_test_util.cc +++ b/mojo/android/javatests/validation_test_util.cc @@ -9,7 +9,7 @@ #include "base/android/scoped_java_ref.h" #include "base/test/test_support_android.h" #include "jni/ValidationTestUtil_jni.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/tests/validation_test_input_parser.h" +#include "mojo/public/cpp/bindings/tests/validation_test_input_parser.h" namespace mojo { namespace android { diff --git a/mojo/android/system/core_impl.cc b/mojo/android/system/core_impl.cc index b474cb6..2158987 100644 --- a/mojo/android/system/core_impl.cc +++ b/mojo/android/system/core_impl.cc @@ -13,9 +13,9 @@ #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "jni/CoreImpl_jni.h" -#include "third_party/mojo/src/mojo/public/c/environment/async_waiter.h" -#include "third_party/mojo/src/mojo/public/c/system/core.h" -#include "third_party/mojo/src/mojo/public/cpp/environment/environment.h" +#include "mojo/public/c/environment/async_waiter.h" +#include "mojo/public/c/system/core.h" +#include "mojo/public/cpp/environment/environment.h" namespace { diff --git a/mojo/application/public/cpp/BUILD.gn b/mojo/application/public/cpp/BUILD.gn index 578463f..fb34910b 100644 --- a/mojo/application/public/cpp/BUILD.gn +++ b/mojo/application/public/cpp/BUILD.gn @@ -50,8 +50,8 @@ source_set("sources") { "//mojo/common", "//mojo/environment:chromium", "//mojo/message_pump", - "//third_party/mojo/src/mojo/public/cpp/bindings", - "//third_party/mojo/src/mojo/public/cpp/system", + "//mojo/public/cpp/bindings", + "//mojo/public/cpp/system", ] } @@ -94,9 +94,9 @@ source_set("test_support") { "//base/test:test_support", "//mojo/application/public/interfaces:interfaces_cpp_sources", "//mojo/logging", - "//third_party/mojo/src/mojo/public/cpp/bindings", - "//third_party/mojo/src/mojo/public/cpp/environment", - "//third_party/mojo/src/mojo/public/cpp/system", + "//mojo/public/cpp/bindings", + "//mojo/public/cpp/environment", + "//mojo/public/cpp/system", "//testing/gtest", ] diff --git a/mojo/application/public/cpp/application_delegate.h b/mojo/application/public/cpp/application_delegate.h index 22deec7..a378490 100644 --- a/mojo/application/public/cpp/application_delegate.h +++ b/mojo/application/public/cpp/application_delegate.h @@ -7,7 +7,7 @@ #include <string> -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { diff --git a/mojo/application/public/cpp/application_impl.h b/mojo/application/public/cpp/application_impl.h index 53ab791..90e7959 100644 --- a/mojo/application/public/cpp/application_impl.h +++ b/mojo/application/public/cpp/application_impl.h @@ -15,9 +15,9 @@ #include "mojo/application/public/cpp/lib/service_registry.h" #include "mojo/application/public/interfaces/application.mojom.h" #include "mojo/application/public/interfaces/shell.mojom.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/callback.h" -#include "third_party/mojo/src/mojo/public/cpp/system/core.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/callback.h" +#include "mojo/public/cpp/system/core.h" namespace mojo { diff --git a/mojo/application/public/cpp/application_runner.h b/mojo/application/public/cpp/application_runner.h index 90329a9..09835cb 100644 --- a/mojo/application/public/cpp/application_runner.h +++ b/mojo/application/public/cpp/application_runner.h @@ -7,7 +7,7 @@ #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" -#include "third_party/mojo/src/mojo/public/cpp/system/core.h" +#include "mojo/public/cpp/system/core.h" namespace mojo { diff --git a/mojo/application/public/cpp/application_test_base.h b/mojo/application/public/cpp/application_test_base.h index d10ee81..5b0fab1 100644 --- a/mojo/application/public/cpp/application_test_base.h +++ b/mojo/application/public/cpp/application_test_base.h @@ -7,10 +7,10 @@ #include "mojo/application/public/cpp/application_delegate.h" #include "mojo/application/public/interfaces/application.mojom.h" +#include "mojo/public/cpp/bindings/array.h" +#include "mojo/public/cpp/bindings/string.h" +#include "mojo/public/cpp/system/macros.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/array.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/string.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" namespace mojo { diff --git a/mojo/application/public/cpp/initialize_base_and_icu.cc b/mojo/application/public/cpp/initialize_base_and_icu.cc index 4635d49..dd65129 100644 --- a/mojo/application/public/cpp/initialize_base_and_icu.cc +++ b/mojo/application/public/cpp/initialize_base_and_icu.cc @@ -11,8 +11,8 @@ #include "base/i18n/icu_util.h" #include "base/rand_util.h" #include "base/sys_info.h" +#include "mojo/public/c/system/types.h" #include "third_party/icu/source/i18n/unicode/timezone.h" -#include "third_party/mojo/src/mojo/public/c/system/types.h" extern "C" { #if defined(WIN32) diff --git a/mojo/application/public/cpp/interface_factory.h b/mojo/application/public/cpp/interface_factory.h index 2dc0cbc..c7dd839 100644 --- a/mojo/application/public/cpp/interface_factory.h +++ b/mojo/application/public/cpp/interface_factory.h @@ -5,7 +5,7 @@ #ifndef MOJO_APPLICATION_PUBLIC_CPP_INTERFACE_FACTORY_H_ #define MOJO_APPLICATION_PUBLIC_CPP_INTERFACE_FACTORY_H_ -#include "third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h" +#include "mojo/public/cpp/bindings/interface_request.h" namespace mojo { diff --git a/mojo/application/public/cpp/lib/application_impl.cc b/mojo/application/public/cpp/lib/application_impl.cc index 7e9781c..6d2fb46 100644 --- a/mojo/application/public/cpp/lib/application_impl.cc +++ b/mojo/application/public/cpp/lib/application_impl.cc @@ -10,8 +10,8 @@ #include "base/message_loop/message_loop.h" #include "mojo/application/public/cpp/application_delegate.h" #include "mojo/application/public/cpp/lib/service_registry.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/interface_ptr.h" -#include "third_party/mojo/src/mojo/public/cpp/environment/logging.h" +#include "mojo/public/cpp/bindings/interface_ptr.h" +#include "mojo/public/cpp/environment/logging.h" namespace mojo { diff --git a/mojo/application/public/cpp/lib/application_test_base.cc b/mojo/application/public/cpp/lib/application_test_base.cc index 8f07f1f..9d6536e 100644 --- a/mojo/application/public/cpp/lib/application_test_base.cc +++ b/mojo/application/public/cpp/lib/application_test_base.cc @@ -8,9 +8,9 @@ #include "base/strings/utf_string_conversions.h" #include "mojo/application/public/cpp/application_impl.h" #include "mojo/application/public/interfaces/application.mojom.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h" -#include "third_party/mojo/src/mojo/public/cpp/environment/environment.h" -#include "third_party/mojo/src/mojo/public/cpp/system/message_pipe.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/environment/environment.h" +#include "mojo/public/cpp/system/message_pipe.h" namespace mojo { namespace test { diff --git a/mojo/application/public/cpp/lib/application_test_main.cc b/mojo/application/public/cpp/lib/application_test_main.cc index 8b4c50b..5d208c3 100644 --- a/mojo/application/public/cpp/lib/application_test_main.cc +++ b/mojo/application/public/cpp/lib/application_test_main.cc @@ -8,7 +8,7 @@ #include "mojo/application/public/cpp/application_runner.h" #include "mojo/application/public/cpp/application_test_base.h" #include "mojo/logging/init_logging.h" -#include "third_party/mojo/src/mojo/public/c/system/main.h" +#include "mojo/public/c/system/main.h" MojoResult MojoMain(MojoHandle handle) { // An AtExitManager instance is needed to construct message loops. diff --git a/mojo/application/public/cpp/lib/content_handler_factory.cc b/mojo/application/public/cpp/lib/content_handler_factory.cc index ccd2333..b01868c 100644 --- a/mojo/application/public/cpp/lib/content_handler_factory.cc +++ b/mojo/application/public/cpp/lib/content_handler_factory.cc @@ -18,7 +18,7 @@ #include "mojo/application/public/cpp/application_runner.h" #include "mojo/application/public/cpp/interface_factory_impl.h" #include "mojo/message_pump/message_pump_mojo.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h" +#include "mojo/public/cpp/bindings/strong_binding.h" namespace mojo { diff --git a/mojo/application/public/cpp/lib/interface_factory_connector.h b/mojo/application/public/cpp/lib/interface_factory_connector.h index 88834a0..8d13bc2 100644 --- a/mojo/application/public/cpp/lib/interface_factory_connector.h +++ b/mojo/application/public/cpp/lib/interface_factory_connector.h @@ -7,7 +7,7 @@ #include "mojo/application/public/cpp/interface_factory.h" #include "mojo/application/public/cpp/service_connector.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h" +#include "mojo/public/cpp/bindings/interface_request.h" namespace mojo { namespace internal { diff --git a/mojo/application/public/cpp/lib/service_connector_registry.h b/mojo/application/public/cpp/lib/service_connector_registry.h index 20ad5ff..b196353 100644 --- a/mojo/application/public/cpp/lib/service_connector_registry.h +++ b/mojo/application/public/cpp/lib/service_connector_registry.h @@ -8,7 +8,7 @@ #include <map> #include <string> -#include "third_party/mojo/src/mojo/public/cpp/system/message_pipe.h" +#include "mojo/public/cpp/system/message_pipe.h" namespace mojo { diff --git a/mojo/application/public/cpp/lib/service_provider_impl.cc b/mojo/application/public/cpp/lib/service_provider_impl.cc index cdddad3..6a9282c 100644 --- a/mojo/application/public/cpp/lib/service_provider_impl.cc +++ b/mojo/application/public/cpp/lib/service_provider_impl.cc @@ -5,7 +5,7 @@ #include "mojo/application/public/cpp/service_provider_impl.h" #include "mojo/application/public/cpp/service_connector.h" -#include "third_party/mojo/src/mojo/public/cpp/environment/logging.h" +#include "mojo/public/cpp/environment/logging.h" namespace mojo { diff --git a/mojo/application/public/cpp/lib/service_registry.h b/mojo/application/public/cpp/lib/service_registry.h index aa209dc..c3610bf 100644 --- a/mojo/application/public/cpp/lib/service_registry.h +++ b/mojo/application/public/cpp/lib/service_registry.h @@ -12,7 +12,7 @@ #include "mojo/application/public/cpp/lib/service_connector_registry.h" #include "mojo/application/public/interfaces/service_provider.mojom.h" #include "mojo/application/public/interfaces/shell.mojom.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/binding.h" namespace mojo { namespace internal { diff --git a/mojo/application/public/cpp/service_connector.h b/mojo/application/public/cpp/service_connector.h index 498d458..3a7d656 100644 --- a/mojo/application/public/cpp/service_connector.h +++ b/mojo/application/public/cpp/service_connector.h @@ -7,7 +7,7 @@ #include <string> -#include "third_party/mojo/src/mojo/public/cpp/system/message_pipe.h" +#include "mojo/public/cpp/system/message_pipe.h" namespace mojo { diff --git a/mojo/application/public/cpp/service_provider_impl.h b/mojo/application/public/cpp/service_provider_impl.h index 2e19a86..0a9fb77 100644 --- a/mojo/application/public/cpp/service_provider_impl.h +++ b/mojo/application/public/cpp/service_provider_impl.h @@ -10,7 +10,7 @@ #include "mojo/application/public/cpp/lib/interface_factory_connector.h" #include "mojo/application/public/cpp/lib/service_connector_registry.h" #include "mojo/application/public/interfaces/service_provider.mojom.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h" +#include "mojo/public/cpp/bindings/strong_binding.h" namespace mojo { diff --git a/mojo/application/public/interfaces/BUILD.gn b/mojo/application/public/interfaces/BUILD.gn index fe107db..f3958b5 100644 --- a/mojo/application/public/interfaces/BUILD.gn +++ b/mojo/application/public/interfaces/BUILD.gn @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import("//third_party/mojo/src/mojo/public/tools/bindings/mojom.gni") +import("//mojo/public/tools/bindings/mojom.gni") # GYP version: mojo/mojo_base.gyp:mojo_application_bindings mojom("interfaces") { diff --git a/mojo/application/public/java/BUILD.gn b/mojo/application/public/java/BUILD.gn index ad25648..7caa8e7 100644 --- a/mojo/application/public/java/BUILD.gn +++ b/mojo/application/public/java/BUILD.gn @@ -15,7 +15,7 @@ android_library("application") { ] deps = [ "//mojo/application/public/interfaces:interfaces_java", - "//third_party/mojo/src/mojo/public/java:bindings", - "//third_party/mojo/src/mojo/public/java:system", + "//mojo/public/java:bindings", + "//mojo/public/java:system", ] } diff --git a/mojo/common/BUILD.gn b/mojo/common/BUILD.gn index eb12b65..7cbd5bf4 100644 --- a/mojo/common/BUILD.gn +++ b/mojo/common/BUILD.gn @@ -32,7 +32,7 @@ component("common_base") { defines = [ "MOJO_COMMON_IMPLEMENTATION" ] public_deps = [ - "//third_party/mojo/src/mojo/public/cpp/system", + "//mojo/public/cpp/system", ] deps = [ @@ -40,9 +40,9 @@ component("common_base") { "//base/third_party/dynamic_annotations", "//mojo/environment:chromium", "//mojo/message_pump", - "//third_party/mojo/src/mojo/public/c/system:for_component", - "//third_party/mojo/src/mojo/public/cpp/bindings", - "//third_party/mojo/src/mojo/public/cpp/environment", + "//mojo/public/c/system:for_component", + "//mojo/public/cpp/bindings", + "//mojo/public/cpp/environment", ] } @@ -59,7 +59,7 @@ source_set("url_type_converters") { ":common_base", "//base", "//base/third_party/dynamic_annotations", - "//third_party/mojo/src/mojo/public/cpp/bindings", + "//mojo/public/cpp/bindings", "//url", ] } @@ -82,11 +82,11 @@ test("mojo_common_unittests") { "//base/test:test_support", "//mojo/environment:chromium", "//mojo/message_pump", + "//mojo/public/cpp/bindings", + "//mojo/public/cpp/test_support:test_utils", "//testing/gtest", "//third_party/mojo/src/mojo/edk/test:run_all_unittests", "//third_party/mojo/src/mojo/edk/test:test_support", - "//third_party/mojo/src/mojo/public/cpp/bindings", - "//third_party/mojo/src/mojo/public/cpp/test_support:test_utils", "//url", ] @@ -105,9 +105,9 @@ test("mojo_common_perftests") { "//base", "//mojo/environment:chromium", "//mojo/message_pump", + "//mojo/public/cpp/test_support:test_utils", "//testing/gtest", "//third_party/mojo/src/mojo/edk/test:run_all_perftests", - "//third_party/mojo/src/mojo/public/cpp/test_support:test_utils", ] sources = [ diff --git a/mojo/common/DEPS b/mojo/common/DEPS index 4ca2066..6dd6560 100644 --- a/mojo/common/DEPS +++ b/mojo/common/DEPS @@ -4,7 +4,7 @@ include_rules = [ "+mojo/application/public/cpp", "+mojo/common", "+mojo/message_pump", - "+third_party/mojo/src/mojo/public", + "+mojo/public", ] specific_include_rules = { diff --git a/mojo/common/common_type_converters.h b/mojo/common/common_type_converters.h index a45f00b..e4ac58e 100644 --- a/mojo/common/common_type_converters.h +++ b/mojo/common/common_type_converters.h @@ -8,9 +8,9 @@ #include "base/strings/string16.h" #include "base/strings/string_piece.h" #include "mojo/common/mojo_common_export.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/array.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/string.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/type_converter.h" +#include "mojo/public/cpp/bindings/array.h" +#include "mojo/public/cpp/bindings/string.h" +#include "mojo/public/cpp/bindings/type_converter.h" namespace mojo { diff --git a/mojo/common/data_pipe_drainer.h b/mojo/common/data_pipe_drainer.h index 19af32d..bf2b031 100644 --- a/mojo/common/data_pipe_drainer.h +++ b/mojo/common/data_pipe_drainer.h @@ -9,7 +9,7 @@ #include "base/memory/weak_ptr.h" #include "mojo/common/mojo_common_export.h" #include "mojo/message_pump/handle_watcher.h" -#include "third_party/mojo/src/mojo/public/cpp/system/core.h" +#include "mojo/public/cpp/system/core.h" namespace mojo { namespace common { diff --git a/mojo/common/data_pipe_utils.h b/mojo/common/data_pipe_utils.h index 5b32cfd..c8cfd8d 100644 --- a/mojo/common/data_pipe_utils.h +++ b/mojo/common/data_pipe_utils.h @@ -9,7 +9,7 @@ #include "base/callback_forward.h" #include "mojo/common/mojo_common_export.h" -#include "third_party/mojo/src/mojo/public/cpp/system/core.h" +#include "mojo/public/cpp/system/core.h" namespace base { class FilePath; diff --git a/mojo/common/url_type_converters.h b/mojo/common/url_type_converters.h index 0439083..1d334a0 100644 --- a/mojo/common/url_type_converters.h +++ b/mojo/common/url_type_converters.h @@ -5,8 +5,8 @@ #ifndef MOJO_COMMON_URL_TYPE_CONVERTERS_H_ #define MOJO_COMMON_URL_TYPE_CONVERTERS_H_ -#include "third_party/mojo/src/mojo/public/cpp/bindings/string.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/type_converter.h" +#include "mojo/public/cpp/bindings/string.h" +#include "mojo/public/cpp/bindings/type_converter.h" class GURL; diff --git a/mojo/common/weak_binding_set.h b/mojo/common/weak_binding_set.h index 7696c82..85c8031 100644 --- a/mojo/common/weak_binding_set.h +++ b/mojo/common/weak_binding_set.h @@ -9,7 +9,7 @@ #include <vector> #include "base/memory/weak_ptr.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/binding.h" namespace mojo { diff --git a/mojo/common/weak_interface_ptr_set.h b/mojo/common/weak_interface_ptr_set.h index 4e64534..e423b93 100644 --- a/mojo/common/weak_interface_ptr_set.h +++ b/mojo/common/weak_interface_ptr_set.h @@ -8,7 +8,7 @@ #include <vector> #include "base/memory/weak_ptr.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/interface_ptr.h" +#include "mojo/public/cpp/bindings/interface_ptr.h" namespace mojo { diff --git a/mojo/converters/geometry/BUILD.gn b/mojo/converters/geometry/BUILD.gn index 58b46e7..efe39c1 100644 --- a/mojo/converters/geometry/BUILD.gn +++ b/mojo/converters/geometry/BUILD.gn @@ -12,7 +12,7 @@ component("geometry") { ] deps = [ "//mojo/environment:chromium", - "//third_party/mojo/src/mojo/public/c/system:for_component", + "//mojo/public/c/system:for_component", "//ui/mojo/geometry:interfaces", ] diff --git a/mojo/converters/ime/BUILD.gn b/mojo/converters/ime/BUILD.gn index 5e9105f..d3c1d86 100644 --- a/mojo/converters/ime/BUILD.gn +++ b/mojo/converters/ime/BUILD.gn @@ -13,7 +13,7 @@ component("ime") { deps = [ "//base", "//mojo/environment:chromium", - "//third_party/mojo/src/mojo/public/c/system:for_component", + "//mojo/public/c/system:for_component", "//ui/mojo/ime:interfaces", "//ui/platform_window", ] diff --git a/mojo/converters/input_events/BUILD.gn b/mojo/converters/input_events/BUILD.gn index 66a5293..fb59cb7 100644 --- a/mojo/converters/input_events/BUILD.gn +++ b/mojo/converters/input_events/BUILD.gn @@ -18,7 +18,7 @@ component("input_events") { "//components/mus/public/interfaces", "//mojo/converters/geometry", "//mojo/environment:chromium", - "//third_party/mojo/src/mojo/public/c/system:for_component", + "//mojo/public/c/system:for_component", "//ui/events", "//ui/events:dom_keycode_converter", "//ui/gfx/geometry", diff --git a/mojo/converters/surfaces/BUILD.gn b/mojo/converters/surfaces/BUILD.gn index 94f93f69..08752a0 100644 --- a/mojo/converters/surfaces/BUILD.gn +++ b/mojo/converters/surfaces/BUILD.gn @@ -32,7 +32,7 @@ component("surfaces") { "//gpu", "//ui/gfx/geometry", "//mojo/environment:chromium", + "//mojo/public/c/system:for_component", "//skia", - "//third_party/mojo/src/mojo/public/c/system:for_component", ] } diff --git a/mojo/converters/transform/BUILD.gn b/mojo/converters/transform/BUILD.gn index da04119..f97791d 100644 --- a/mojo/converters/transform/BUILD.gn +++ b/mojo/converters/transform/BUILD.gn @@ -11,7 +11,7 @@ component("transform") { deps = [ "//skia", "//mojo/environment:chromium", - "//third_party/mojo/src/mojo/public/c/system:for_component", + "//mojo/public/c/system:for_component", "//ui/mojo/geometry:interfaces", ] diff --git a/mojo/edk/embedder/BUILD.gn b/mojo/edk/embedder/BUILD.gn index a90aeb7..feb1b63 100644 --- a/mojo/edk/embedder/BUILD.gn +++ b/mojo/edk/embedder/BUILD.gn @@ -37,7 +37,7 @@ mojo_edk_source_set("embedder") { public_deps = [ ":delegates", ":platform", - "//third_party/mojo/src/mojo/public/cpp/system", + "//mojo/public/cpp/system", ] deps = [ @@ -83,7 +83,7 @@ mojo_edk_source_set("platform") { configs = [ "//mojo/edk/system:system_config" ] public_deps = [ - "//third_party/mojo/src/mojo/public/cpp/system", + "//mojo/public/cpp/system", ] deps = [ @@ -112,7 +112,7 @@ mojo_edk_source_set("delegates") { configs = [ "//mojo/edk/system:system_config" ] public_deps = [ - "//third_party/mojo/src/mojo/public/cpp/system", + "//mojo/public/cpp/system", ] } diff --git a/mojo/edk/embedder/embedder.h b/mojo/edk/embedder/embedder.h index 3c0dddf..2bf9e15 100644 --- a/mojo/edk/embedder/embedder.h +++ b/mojo/edk/embedder/embedder.h @@ -13,7 +13,7 @@ #include "base/task_runner.h" #include "mojo/edk/embedder/scoped_platform_handle.h" #include "mojo/edk/system/system_impl_export.h" -#include "third_party/mojo/src/mojo/public/cpp/system/message_pipe.h" +#include "mojo/public/cpp/system/message_pipe.h" namespace mojo { namespace edk { diff --git a/mojo/edk/embedder/embedder_unittest.cc b/mojo/edk/embedder/embedder_unittest.cc index 659f0ee..f53e440 100644 --- a/mojo/edk/embedder/embedder_unittest.cc +++ b/mojo/edk/embedder/embedder_unittest.cc @@ -17,11 +17,11 @@ #include "mojo/edk/system/test_utils.h" #include "mojo/edk/test/multiprocess_test_helper.h" #include "mojo/message_pump/message_pump_mojo.h" +#include "mojo/public/c/system/core.h" +#include "mojo/public/cpp/system/handle.h" +#include "mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/system/message_pipe.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/mojo/src/mojo/public/c/system/core.h" -#include "third_party/mojo/src/mojo/public/cpp/system/handle.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" -#include "third_party/mojo/src/mojo/public/cpp/system/message_pipe.h" namespace mojo { namespace edk { diff --git a/mojo/edk/embedder/entrypoints.cc b/mojo/edk/embedder/entrypoints.cc index b491265..6247c5f 100644 --- a/mojo/edk/embedder/entrypoints.cc +++ b/mojo/edk/embedder/entrypoints.cc @@ -4,10 +4,10 @@ #include "mojo/edk/embedder/embedder_internal.h" #include "mojo/edk/system/core.h" -#include "third_party/mojo/src/mojo/public/c/system/buffer.h" -#include "third_party/mojo/src/mojo/public/c/system/data_pipe.h" -#include "third_party/mojo/src/mojo/public/c/system/functions.h" -#include "third_party/mojo/src/mojo/public/c/system/message_pipe.h" +#include "mojo/public/c/system/buffer.h" +#include "mojo/public/c/system/data_pipe.h" +#include "mojo/public/c/system/functions.h" +#include "mojo/public/c/system/message_pipe.h" using mojo::edk::internal::g_core; diff --git a/mojo/edk/embedder/platform_channel_pair.h b/mojo/edk/embedder/platform_channel_pair.h index 491c602..7487837 100644 --- a/mojo/edk/embedder/platform_channel_pair.h +++ b/mojo/edk/embedder/platform_channel_pair.h @@ -10,7 +10,7 @@ #include "build/build_config.h" #include "mojo/edk/embedder/scoped_platform_handle.h" #include "mojo/edk/system/system_impl_export.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/system/macros.h" namespace base { class CommandLine; diff --git a/mojo/edk/embedder/platform_channel_pair_posix_unittest.cc b/mojo/edk/embedder/platform_channel_pair_posix_unittest.cc index cd9d5fe..a0bb7fd 100644 --- a/mojo/edk/embedder/platform_channel_pair_posix_unittest.cc +++ b/mojo/edk/embedder/platform_channel_pair_posix_unittest.cc @@ -25,8 +25,8 @@ #include "mojo/edk/embedder/platform_handle_vector.h" #include "mojo/edk/embedder/scoped_platform_handle.h" #include "mojo/edk/test/test_utils.h" +#include "mojo/public/cpp/system/macros.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/embedder/platform_shared_buffer.h b/mojo/edk/embedder/platform_shared_buffer.h index 909cd76..c7256af 100644 --- a/mojo/edk/embedder/platform_shared_buffer.h +++ b/mojo/edk/embedder/platform_shared_buffer.h @@ -11,7 +11,7 @@ #include "base/memory/scoped_ptr.h" #include "mojo/edk/embedder/scoped_platform_handle.h" #include "mojo/edk/system/system_impl_export.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/embedder/platform_support.h b/mojo/edk/embedder/platform_support.h index 15a300c..160ba79 100644 --- a/mojo/edk/embedder/platform_support.h +++ b/mojo/edk/embedder/platform_support.h @@ -9,7 +9,7 @@ #include "mojo/edk/embedder/scoped_platform_handle.h" #include "mojo/edk/system/system_impl_export.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/embedder/process_delegate.h b/mojo/edk/embedder/process_delegate.h index d8cebc3..e5f90af 100644 --- a/mojo/edk/embedder/process_delegate.h +++ b/mojo/edk/embedder/process_delegate.h @@ -6,7 +6,7 @@ #define MOJO_EDK_EMBEDDER_PROCESS_DELEGATE_H_ #include "mojo/edk/system/system_impl_export.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/embedder/scoped_platform_handle.h b/mojo/edk/embedder/scoped_platform_handle.h index ce8e28a..ae75202 100644 --- a/mojo/edk/embedder/scoped_platform_handle.h +++ b/mojo/edk/embedder/scoped_platform_handle.h @@ -8,7 +8,7 @@ #include "base/move.h" #include "mojo/edk/embedder/platform_handle.h" #include "mojo/edk/system/system_impl_export.h" -#include "third_party/mojo/src/mojo/public/c/system/macros.h" +#include "mojo/public/c/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/embedder/simple_platform_shared_buffer.h b/mojo/edk/embedder/simple_platform_shared_buffer.h index 3235b0b..798bc3a 100644 --- a/mojo/edk/embedder/simple_platform_shared_buffer.h +++ b/mojo/edk/embedder/simple_platform_shared_buffer.h @@ -9,7 +9,7 @@ #include "mojo/edk/embedder/platform_shared_buffer.h" #include "mojo/edk/system/system_impl_export.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/embedder/simple_platform_shared_buffer_unittest.cc b/mojo/edk/embedder/simple_platform_shared_buffer_unittest.cc index 9f174ea..a0e915e 100644 --- a/mojo/edk/embedder/simple_platform_shared_buffer_unittest.cc +++ b/mojo/edk/embedder/simple_platform_shared_buffer_unittest.cc @@ -8,8 +8,8 @@ #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "mojo/public/cpp/system/macros.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/embedder/simple_platform_support.h b/mojo/edk/embedder/simple_platform_support.h index 91bdce0..ef97d91 100644 --- a/mojo/edk/embedder/simple_platform_support.h +++ b/mojo/edk/embedder/simple_platform_support.h @@ -7,7 +7,7 @@ #include "mojo/edk/embedder/platform_support.h" #include "mojo/edk/system/system_impl_export.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/js/BUILD.gn b/mojo/edk/js/BUILD.gn index dc61068e..0c3f6ad 100644 --- a/mojo/edk/js/BUILD.gn +++ b/mojo/edk/js/BUILD.gn @@ -45,8 +45,8 @@ mojo_edk_source_set("js") { ] deps = [ - "//third_party/mojo/src/mojo/public/cpp/environment", - "//third_party/mojo/src/mojo/public/cpp/system", + "//mojo/public/cpp/environment", + "//mojo/public/cpp/system", ] } @@ -59,7 +59,7 @@ mojo_edk_source_set("js_unittests") { deps = [ "//mojo/edk/js", "//mojo/edk/test:test_support", + "//mojo/public/cpp/system", "//testing/gtest", - "//third_party/mojo/src/mojo/public/cpp/system", ] } diff --git a/mojo/edk/js/drain_data.cc b/mojo/edk/js/drain_data.cc index f5e6f89..d8fa091 100644 --- a/mojo/edk/js/drain_data.cc +++ b/mojo/edk/js/drain_data.cc @@ -9,8 +9,8 @@ #include "gin/dictionary.h" #include "gin/per_context_data.h" #include "gin/per_isolate_data.h" -#include "third_party/mojo/src/mojo/public/cpp/environment/environment.h" -#include "third_party/mojo/src/mojo/public/cpp/system/core.h" +#include "mojo/public/cpp/environment/environment.h" +#include "mojo/public/cpp/system/core.h" namespace mojo { namespace edk { diff --git a/mojo/edk/js/drain_data.h b/mojo/edk/js/drain_data.h index 839241f..63c9e49 100644 --- a/mojo/edk/js/drain_data.h +++ b/mojo/edk/js/drain_data.h @@ -7,8 +7,8 @@ #include "base/memory/scoped_vector.h" #include "gin/runner.h" -#include "third_party/mojo/src/mojo/public/c/environment/async_waiter.h" -#include "third_party/mojo/src/mojo/public/cpp/system/core.h" +#include "mojo/public/c/environment/async_waiter.h" +#include "mojo/public/cpp/system/core.h" #include "v8/include/v8.h" namespace mojo { diff --git a/mojo/edk/js/handle.h b/mojo/edk/js/handle.h index 4257816..9fa92b0 100644 --- a/mojo/edk/js/handle.h +++ b/mojo/edk/js/handle.h @@ -9,7 +9,7 @@ #include "gin/converter.h" #include "gin/handle.h" #include "gin/wrappable.h" -#include "third_party/mojo/src/mojo/public/cpp/system/core.h" +#include "mojo/public/cpp/system/core.h" namespace mojo { namespace edk { diff --git a/mojo/edk/js/handle_unittest.cc b/mojo/edk/js/handle_unittest.cc index ff15154..6ee4444 100644 --- a/mojo/edk/js/handle_unittest.cc +++ b/mojo/edk/js/handle_unittest.cc @@ -5,8 +5,8 @@ #include "base/macros.h" #include "mojo/edk/js/handle.h" #include "mojo/edk/js/handle_close_observer.h" +#include "mojo/public/cpp/system/core.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/mojo/src/mojo/public/cpp/system/core.h" namespace mojo { namespace edk { diff --git a/mojo/edk/js/mojo_runner_delegate.h b/mojo/edk/js/mojo_runner_delegate.h index 9a66d88..7b50b30 100644 --- a/mojo/edk/js/mojo_runner_delegate.h +++ b/mojo/edk/js/mojo_runner_delegate.h @@ -7,7 +7,7 @@ #include "base/macros.h" #include "gin/modules/module_runner_delegate.h" -#include "third_party/mojo/src/mojo/public/c/system/core.h" +#include "mojo/public/c/system/core.h" namespace mojo { namespace edk { diff --git a/mojo/edk/js/support.cc b/mojo/edk/js/support.cc index 0e9708f..3e4391c 100644 --- a/mojo/edk/js/support.cc +++ b/mojo/edk/js/support.cc @@ -14,7 +14,7 @@ #include "gin/wrappable.h" #include "mojo/edk/js/handle.h" #include "mojo/edk/js/waiting_callback.h" -#include "third_party/mojo/src/mojo/public/cpp/system/core.h" +#include "mojo/public/cpp/system/core.h" namespace mojo { namespace edk { diff --git a/mojo/edk/js/test/BUILD.gn b/mojo/edk/js/test/BUILD.gn index 86390ba..e41e889 100644 --- a/mojo/edk/js/test/BUILD.gn +++ b/mojo/edk/js/test/BUILD.gn @@ -14,11 +14,11 @@ test("js_unittests2") { "//base", "//gin:gin_test", "//mojo/environment:chromium", - "//third_party/mojo/src/mojo/public/cpp/environment", - "//third_party/mojo/src/mojo/public/cpp/system", - "//third_party/mojo/src/mojo/public/cpp/utility", - "//third_party/mojo/src/mojo/public/interfaces/bindings/tests:test_interfaces", - "//third_party/mojo/src/mojo/public/interfaces/bindings/tests:test_interfaces_experimental", + "//mojo/public/cpp/environment", + "//mojo/public/cpp/system", + "//mojo/public/cpp/utility", + "//mojo/public/interfaces/bindings/tests:test_interfaces", + "//mojo/public/interfaces/bindings/tests:test_interfaces_experimental", ] sources = [ @@ -33,11 +33,11 @@ test("js_integration_tests2") { "../../js/tests:js_to_cpp_tests", "../../test:run_all_unittests", "../../test:test_support", - "//third_party/mojo/src/mojo/public/cpp/bindings", - "//third_party/mojo/src/mojo/public/interfaces/bindings/tests:test_interfaces", "//base", "//gin:gin_test", "//mojo/environment:chromium", + "//mojo/public/cpp/bindings", + "//mojo/public/interfaces/bindings/tests:test_interfaces", ] sources = [ diff --git a/mojo/edk/js/test/run_js_tests.cc b/mojo/edk/js/test/run_js_tests.cc index 1505527..76de3e6 100644 --- a/mojo/edk/js/test/run_js_tests.cc +++ b/mojo/edk/js/test/run_js_tests.cc @@ -10,8 +10,8 @@ #include "gin/test/gtest.h" #include "mojo/edk/js/core.h" #include "mojo/edk/js/support.h" +#include "mojo/public/cpp/environment/environment.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/mojo/src/mojo/public/cpp/environment/environment.h" namespace mojo { namespace edk { diff --git a/mojo/edk/js/tests/BUILD.gn b/mojo/edk/js/tests/BUILD.gn index ee2af69..8d4a959 100644 --- a/mojo/edk/js/tests/BUILD.gn +++ b/mojo/edk/js/tests/BUILD.gn @@ -3,7 +3,7 @@ # found in the LICENSE file. import("../../mojo_edk.gni") -import("../../../../third_party/mojo/src/mojo/public/tools/bindings/mojom.gni") +import("../../../../mojo/public/tools/bindings/mojom.gni") mojo_edk_source_set("js_to_cpp_tests") { testonly = true @@ -13,10 +13,10 @@ mojo_edk_source_set("js_to_cpp_tests") { "//gin:gin_test", "//mojo/edk/js", "//mojo/edk/test:test_support", - "//third_party/mojo/src/mojo/public/cpp/bindings", - "//third_party/mojo/src/mojo/public/cpp/system", - "//third_party/mojo/src/mojo/public/interfaces/bindings/tests:test_interfaces", - "//third_party/mojo/src/mojo/public/interfaces/bindings/tests:test_interfaces_experimental", + "//mojo/public/cpp/bindings", + "//mojo/public/cpp/system", + "//mojo/public/interfaces/bindings/tests:test_interfaces", + "//mojo/public/interfaces/bindings/tests:test_interfaces_experimental", ] sources = [ diff --git a/mojo/edk/js/tests/js_to_cpp_tests.cc b/mojo/edk/js/tests/js_to_cpp_tests.cc index 97002f1..f9a9b92 100644 --- a/mojo/edk/js/tests/js_to_cpp_tests.cc +++ b/mojo/edk/js/tests/js_to_cpp_tests.cc @@ -14,10 +14,10 @@ #include "mojo/edk/js/mojo_runner_delegate.h" #include "mojo/edk/js/tests/js_to_cpp.mojom.h" #include "mojo/edk/test/test_utils.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/system/core.h" +#include "mojo/public/cpp/system/macros.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h" -#include "third_party/mojo/src/mojo/public/cpp/system/core.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/js/waiting_callback.cc b/mojo/edk/js/waiting_callback.cc index 53345ac..4d017fe 100644 --- a/mojo/edk/js/waiting_callback.cc +++ b/mojo/edk/js/waiting_callback.cc @@ -7,7 +7,7 @@ #include "base/bind.h" #include "base/message_loop/message_loop.h" #include "gin/per_context_data.h" -#include "third_party/mojo/src/mojo/public/cpp/environment/environment.h" +#include "mojo/public/cpp/environment/environment.h" namespace mojo { namespace edk { diff --git a/mojo/edk/js/waiting_callback.h b/mojo/edk/js/waiting_callback.h index 2deedfc..c1b76aa 100644 --- a/mojo/edk/js/waiting_callback.h +++ b/mojo/edk/js/waiting_callback.h @@ -11,8 +11,8 @@ #include "gin/wrappable.h" #include "mojo/edk/js/handle.h" #include "mojo/edk/js/handle_close_observer.h" -#include "third_party/mojo/src/mojo/public/c/environment/async_waiter.h" -#include "third_party/mojo/src/mojo/public/cpp/system/core.h" +#include "mojo/public/c/environment/async_waiter.h" +#include "mojo/public/cpp/system/core.h" namespace mojo { namespace edk { diff --git a/mojo/edk/mojo_edk.gni b/mojo/edk/mojo_edk.gni index efb2a5f..60d6e9f 100644 --- a/mojo/edk/mojo_edk.gni +++ b/mojo/edk/mojo_edk.gni @@ -2,13 +2,12 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import("../../third_party/mojo/src/mojo/public/mojo_sdk.gni") +import("../../mojo/public/mojo_sdk.gni") # A mojo_edk_source_set is a mojo_sdk_source_set that does not restrict # external dependencies and understands the following additional variables, all # of which admit a list of the relevant elements specified relative to the # location of the Mojo EDK: -# allow_circular_mojo_edk_includes_from template("mojo_edk_source_set") { mojo_sdk_source_set(target_name) { if (defined(invoker.public_deps) || defined(invoker.deps)) { @@ -40,15 +39,6 @@ template("mojo_edk_source_set") { if (defined(invoker.allow_circular_includes_from)) { allow_circular_includes_from += invoker.allow_circular_includes_from } - if (defined(invoker.allow_circular_mojo_edk_includes_from)) { - foreach(edk_target, invoker.allow_circular_mojo_edk_includes_from) { - # Check that the EDK target was not mistakenly given as an absolute - # path. - assert(get_path_info(edk_target, "abspath") != edk_target) - allow_circular_includes_from += - [ rebase_path(edk_target, ".", mojo_root) ] - } - } if (defined(invoker.public_deps)) { public_deps = invoker.public_deps diff --git a/mojo/edk/system/BUILD.gn b/mojo/edk/system/BUILD.gn index 577a072..358a9fa 100644 --- a/mojo/edk/system/BUILD.gn +++ b/mojo/edk/system/BUILD.gn @@ -4,7 +4,7 @@ import("../mojo_edk.gni") import("//testing/test.gni") -import("../../../third_party/mojo/src/mojo/public/tools/bindings/mojom.gni") +import("../../../mojo/public/tools/bindings/mojom.gni") if (is_android) { import("//build/config/android/config.gni") @@ -94,8 +94,8 @@ static_library("system") { "../embedder", "../embedder:delegates", "../embedder:platform", - "../../../third_party/mojo/src/mojo/public/c/system", - "../../../third_party/mojo/src/mojo/public/cpp/system", + "../../../mojo/public/c/system", + "../../../mojo/public/cpp/system", ] deps = [ @@ -131,8 +131,8 @@ mojo_edk_source_set("test_utils") { ] public_deps = [ - "//third_party/mojo/src/mojo/public/c/system", - "//third_party/mojo/src/mojo/public/cpp/system", + "//mojo/public/c/system", + "//mojo/public/cpp/system", ] deps = [ @@ -179,7 +179,7 @@ test("mojo_system_unittests2") { # TODO(use_chrome_edk): remove "2" "../embedder:embedder_unittests2", - "../../../third_party/mojo/src/mojo/public/cpp/environment:standalone", + "../../../mojo/public/cpp/environment:standalone", "../test:test_support", "//base", "//base/test:test_support", @@ -206,7 +206,7 @@ test("mojo_message_pipe_perftests2") { ":test_utils", "../test:test_support", "../test:run_all_perftests", - "../../../third_party/mojo/src/mojo/public/cpp/environment:standalone", + "../../../mojo/public/cpp/environment:standalone", "//base", "//base/test:test_support", "//testing/gtest", diff --git a/mojo/edk/system/async_waiter.h b/mojo/edk/system/async_waiter.h index b8eefa0..19ef5d6 100644 --- a/mojo/edk/system/async_waiter.h +++ b/mojo/edk/system/async_waiter.h @@ -8,8 +8,8 @@ #include "base/callback.h" #include "mojo/edk/system/awakable.h" #include "mojo/edk/system/system_impl_export.h" -#include "third_party/mojo/src/mojo/public/c/system/types.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/c/system/types.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/awakable.h b/mojo/edk/system/awakable.h index a99038f..2cb10f5 100644 --- a/mojo/edk/system/awakable.h +++ b/mojo/edk/system/awakable.h @@ -8,7 +8,7 @@ #include <stdint.h> #include "mojo/edk/system/system_impl_export.h" -#include "third_party/mojo/src/mojo/public/c/system/types.h" +#include "mojo/public/c/system/types.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/awakable_list.h b/mojo/edk/system/awakable_list.h index 40ce9ad..4e788ee 100644 --- a/mojo/edk/system/awakable_list.h +++ b/mojo/edk/system/awakable_list.h @@ -10,8 +10,8 @@ #include <vector> #include "mojo/edk/system/system_impl_export.h" -#include "third_party/mojo/src/mojo/public/c/system/types.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/c/system/types.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/core.cc b/mojo/edk/system/core.cc index fe9d09f..ac513da 100644 --- a/mojo/edk/system/core.cc +++ b/mojo/edk/system/core.cc @@ -21,8 +21,8 @@ #include "mojo/edk/system/message_pipe_dispatcher.h" #include "mojo/edk/system/shared_buffer_dispatcher.h" #include "mojo/edk/system/waiter.h" -#include "third_party/mojo/src/mojo/public/c/system/macros.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/c/system/macros.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/core.h b/mojo/edk/system/core.h index 1e2cd3f..3c2154a 100644 --- a/mojo/edk/system/core.h +++ b/mojo/edk/system/core.h @@ -14,11 +14,11 @@ #include "mojo/edk/system/handle_table.h" #include "mojo/edk/system/mapping_table.h" #include "mojo/edk/system/system_impl_export.h" -#include "third_party/mojo/src/mojo/public/c/system/buffer.h" -#include "third_party/mojo/src/mojo/public/c/system/data_pipe.h" -#include "third_party/mojo/src/mojo/public/c/system/message_pipe.h" -#include "third_party/mojo/src/mojo/public/c/system/types.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/c/system/buffer.h" +#include "mojo/public/c/system/data_pipe.h" +#include "mojo/public/c/system/message_pipe.h" +#include "mojo/public/c/system/types.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { @@ -69,7 +69,7 @@ class MOJO_SYSTEM_IMPL_EXPORT Core { // API functions, referenced below. // These methods correspond to the API functions defined in - // "third_party/mojo/src/mojo/public/c/system/functions.h": + // "mojo/public/c/system/functions.h": MojoTimeTicks GetTimeTicksNow(); MojoResult Close(MojoHandle handle); MojoResult Wait(MojoHandle handle, @@ -84,7 +84,7 @@ class MOJO_SYSTEM_IMPL_EXPORT Core { MojoHandleSignalsState* signals_states); // These methods correspond to the API functions defined in - // "third_party/mojo/src/mojo/public/c/system/message_pipe.h": + // "mojo/public/c/system/message_pipe.h": MojoResult CreateMessagePipe( const MojoCreateMessagePipeOptions* options, MojoHandle* message_pipe_handle0, @@ -103,7 +103,7 @@ class MOJO_SYSTEM_IMPL_EXPORT Core { MojoReadMessageFlags flags); // These methods correspond to the API functions defined in - // "third_party/mojo/src/mojo/public/c/system/data_pipe.h": + // "mojo/public/c/system/data_pipe.h": MojoResult CreateDataPipe( const MojoCreateDataPipeOptions* options, MojoHandle* data_pipe_producer_handle, @@ -130,7 +130,7 @@ class MOJO_SYSTEM_IMPL_EXPORT Core { uint32_t num_bytes_read); // These methods correspond to the API functions defined in - // "third_party/mojo/src/mojo/public/c/system/buffer.h": + // "mojo/public/c/system/buffer.h": MojoResult CreateSharedBuffer( const MojoCreateSharedBufferOptions* options, uint64_t num_bytes, diff --git a/mojo/edk/system/core_test_base.cc b/mojo/edk/system/core_test_base.cc index e9b5589..2a2851b 100644 --- a/mojo/edk/system/core_test_base.cc +++ b/mojo/edk/system/core_test_base.cc @@ -11,7 +11,7 @@ #include "mojo/edk/system/configuration.h" #include "mojo/edk/system/core.h" #include "mojo/edk/system/dispatcher.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/core_test_base.h b/mojo/edk/system/core_test_base.h index 84bc2e6..774dc95 100644 --- a/mojo/edk/system/core_test_base.h +++ b/mojo/edk/system/core_test_base.h @@ -8,9 +8,9 @@ #include "base/synchronization/lock.h" #include "mojo/edk/embedder/embedder_internal.h" #include "mojo/edk/system/test_utils.h" +#include "mojo/public/c/system/types.h" +#include "mojo/public/cpp/system/macros.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/mojo/src/mojo/public/c/system/types.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/core_unittest.cc b/mojo/edk/system/core_unittest.cc index 6dfadee..28a038c 100644 --- a/mojo/edk/system/core_unittest.cc +++ b/mojo/edk/system/core_unittest.cc @@ -13,7 +13,7 @@ #include "mojo/edk/system/awakable.h" #include "mojo/edk/system/core_test_base.h" #include "mojo/edk/system/test_utils.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/data_pipe.h b/mojo/edk/system/data_pipe.h index 46a895d..f17416b 100644 --- a/mojo/edk/system/data_pipe.h +++ b/mojo/edk/system/data_pipe.h @@ -9,9 +9,9 @@ #include "mojo/edk/embedder/platform_handle_vector.h" #include "mojo/edk/embedder/scoped_platform_handle.h" #include "mojo/edk/system/system_impl_export.h" -#include "third_party/mojo/src/mojo/public/c/system/data_pipe.h" -#include "third_party/mojo/src/mojo/public/c/system/types.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/c/system/data_pipe.h" +#include "mojo/public/c/system/types.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/data_pipe_consumer_dispatcher.h b/mojo/edk/system/data_pipe_consumer_dispatcher.h index c172c5b..0b385ba 100644 --- a/mojo/edk/system/data_pipe_consumer_dispatcher.h +++ b/mojo/edk/system/data_pipe_consumer_dispatcher.h @@ -10,7 +10,7 @@ #include "mojo/edk/system/dispatcher.h" #include "mojo/edk/system/raw_channel.h" #include "mojo/edk/system/system_impl_export.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/data_pipe_producer_dispatcher.h b/mojo/edk/system/data_pipe_producer_dispatcher.h index f07c828..e387d2f 100644 --- a/mojo/edk/system/data_pipe_producer_dispatcher.h +++ b/mojo/edk/system/data_pipe_producer_dispatcher.h @@ -10,7 +10,7 @@ #include "mojo/edk/system/dispatcher.h" #include "mojo/edk/system/raw_channel.h" #include "mojo/edk/system/system_impl_export.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/data_pipe_unittest.cc b/mojo/edk/system/data_pipe_unittest.cc index 6c6593e..5c8f0f8 100644 --- a/mojo/edk/system/data_pipe_unittest.cc +++ b/mojo/edk/system/data_pipe_unittest.cc @@ -13,11 +13,11 @@ #include "mojo/edk/embedder/simple_platform_support.h" #include "mojo/edk/system/test_utils.h" #include "mojo/edk/system/waiter.h" +#include "mojo/public/c/system/data_pipe.h" +#include "mojo/public/c/system/functions.h" +#include "mojo/public/c/system/message_pipe.h" +#include "mojo/public/cpp/system/macros.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/mojo/src/mojo/public/c/system/data_pipe.h" -#include "third_party/mojo/src/mojo/public/c/system/functions.h" -#include "third_party/mojo/src/mojo/public/c/system/message_pipe.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/dispatcher.h b/mojo/edk/system/dispatcher.h index df1e975..fb757c7 100644 --- a/mojo/edk/system/dispatcher.h +++ b/mojo/edk/system/dispatcher.h @@ -17,11 +17,11 @@ #include "mojo/edk/embedder/platform_handle_vector.h" #include "mojo/edk/system/handle_signals_state.h" #include "mojo/edk/system/system_impl_export.h" -#include "third_party/mojo/src/mojo/public/c/system/buffer.h" -#include "third_party/mojo/src/mojo/public/c/system/data_pipe.h" -#include "third_party/mojo/src/mojo/public/c/system/message_pipe.h" -#include "third_party/mojo/src/mojo/public/c/system/types.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/c/system/buffer.h" +#include "mojo/public/c/system/data_pipe.h" +#include "mojo/public/c/system/message_pipe.h" +#include "mojo/public/c/system/types.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/dispatcher_unittest.cc b/mojo/edk/system/dispatcher_unittest.cc index 7145eb3..8b3017e 100644 --- a/mojo/edk/system/dispatcher_unittest.cc +++ b/mojo/edk/system/dispatcher_unittest.cc @@ -10,8 +10,8 @@ #include "base/threading/simple_thread.h" #include "mojo/edk/embedder/platform_shared_buffer.h" #include "mojo/edk/system/waiter.h" +#include "mojo/public/cpp/system/macros.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/handle_signals_state.h b/mojo/edk/system/handle_signals_state.h index 2209ffa..1c47a28 100644 --- a/mojo/edk/system/handle_signals_state.h +++ b/mojo/edk/system/handle_signals_state.h @@ -6,7 +6,7 @@ #define MOJO_EDK_SYSTEM_HANDLE_SIGNALS_STATE_H_ #include "mojo/edk/system/system_impl_export.h" -#include "third_party/mojo/src/mojo/public/c/system/types.h" +#include "mojo/public/c/system/types.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/handle_table.h b/mojo/edk/system/handle_table.h index 57d1aed..de5ac05 100644 --- a/mojo/edk/system/handle_table.h +++ b/mojo/edk/system/handle_table.h @@ -11,8 +11,8 @@ #include "base/containers/hash_tables.h" #include "base/memory/ref_counted.h" #include "mojo/edk/system/system_impl_export.h" -#include "third_party/mojo/src/mojo/public/c/system/types.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/c/system/types.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/mapping_table.h b/mojo/edk/system/mapping_table.h index fd822989..fb2acf3 100644 --- a/mojo/edk/system/mapping_table.h +++ b/mojo/edk/system/mapping_table.h @@ -12,8 +12,8 @@ #include "base/containers/hash_tables.h" #include "base/memory/scoped_ptr.h" #include "mojo/edk/system/system_impl_export.h" -#include "third_party/mojo/src/mojo/public/c/system/types.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/c/system/types.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { diff --git a/mojo/edk/system/master_impl.h b/mojo/edk/system/master_impl.h index d6d1a8d..621db13 100644 --- a/mojo/edk/system/master_impl.h +++ b/mojo/edk/system/master_impl.h @@ -8,7 +8,7 @@ #include "base/process/process.h" #include "mojo/edk/system/master.mojom.h" #include "mojo/edk/system/system_impl_export.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/system/macros.h" #if defined(OS_WIN) #include <windows.h> diff --git a/mojo/edk/system/master_impl_unittest.cc b/mojo/edk/system/master_impl_unittest.cc index 04d656e..a0a731e 100644 --- a/mojo/edk/system/master_impl_unittest.cc +++ b/mojo/edk/system/master_impl_unittest.cc @@ -10,8 +10,8 @@ #include "base/files/scoped_temp_dir.h" #include "mojo/edk/embedder/embedder.h" #include "mojo/edk/test/test_utils.h" +#include "mojo/public/c/system/macros.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/mojo/src/mojo/public/c/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/message_in_transit.h b/mojo/edk/system/message_in_transit.h index 34396b8..78dc40a 100644 --- a/mojo/edk/system/message_in_transit.h +++ b/mojo/edk/system/message_in_transit.h @@ -15,7 +15,7 @@ #include "base/memory/scoped_ptr.h" #include "mojo/edk/system/dispatcher.h" #include "mojo/edk/system/system_impl_export.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/message_in_transit_queue.h b/mojo/edk/system/message_in_transit_queue.h index a6e6adb..4a05490 100644 --- a/mojo/edk/system/message_in_transit_queue.h +++ b/mojo/edk/system/message_in_transit_queue.h @@ -10,7 +10,7 @@ #include "base/memory/scoped_ptr.h" #include "mojo/edk/system/message_in_transit.h" #include "mojo/edk/system/system_impl_export.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/message_pipe_dispatcher.h b/mojo/edk/system/message_pipe_dispatcher.h index fa7b6e7..5248583 100644 --- a/mojo/edk/system/message_pipe_dispatcher.h +++ b/mojo/edk/system/message_pipe_dispatcher.h @@ -11,7 +11,7 @@ #include "mojo/edk/system/dispatcher.h" #include "mojo/edk/system/raw_channel.h" #include "mojo/edk/system/system_impl_export.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/message_pipe_perftest.cc b/mojo/edk/system/message_pipe_perftest.cc index 053b000..5d59e65 100644 --- a/mojo/edk/system/message_pipe_perftest.cc +++ b/mojo/edk/system/message_pipe_perftest.cc @@ -12,9 +12,9 @@ #include "mojo/edk/system/message_pipe_test_utils.h" #include "mojo/edk/system/test_utils.h" #include "mojo/edk/test/test_utils.h" +#include "mojo/public/c/system/functions.h" +#include "mojo/public/cpp/system/message_pipe.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/mojo/src/mojo/public/c/system/functions.h" -#include "third_party/mojo/src/mojo/public/cpp/system/message_pipe.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/message_pipe_test_utils.h b/mojo/edk/system/message_pipe_test_utils.h index ae6f61a..e067329 100644 --- a/mojo/edk/system/message_pipe_test_utils.h +++ b/mojo/edk/system/message_pipe_test_utils.h @@ -11,7 +11,7 @@ #include "mojo/edk/system/test_utils.h" #include "mojo/edk/test/multiprocess_test_helper.h" #include "mojo/edk/test/scoped_ipc_support.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/message_pipe_unittest.cc b/mojo/edk/system/message_pipe_unittest.cc index 17e6b62..9283acc 100644 --- a/mojo/edk/system/message_pipe_unittest.cc +++ b/mojo/edk/system/message_pipe_unittest.cc @@ -4,8 +4,8 @@ #include "base/memory/ref_counted.h" #include "mojo/edk/system/test_utils.h" -#include "third_party/mojo/src/mojo/public/c/system/core.h" -#include "third_party/mojo/src/mojo/public/c/system/types.h" +#include "mojo/public/c/system/core.h" +#include "mojo/public/c/system/types.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/options_validation.h b/mojo/edk/system/options_validation.h index 2299f61..6925075 100644 --- a/mojo/edk/system/options_validation.h +++ b/mojo/edk/system/options_validation.h @@ -18,8 +18,8 @@ #include "base/logging.h" #include "mojo/edk/system/system_impl_export.h" -#include "third_party/mojo/src/mojo/public/c/system/types.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/c/system/types.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/options_validation_unittest.cc b/mojo/edk/system/options_validation_unittest.cc index bd4e14d..d2c8180 100644 --- a/mojo/edk/system/options_validation_unittest.cc +++ b/mojo/edk/system/options_validation_unittest.cc @@ -7,8 +7,8 @@ #include <stddef.h> #include <stdint.h> +#include "mojo/public/c/system/macros.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/mojo/src/mojo/public/c/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/platform_handle_dispatcher.h b/mojo/edk/system/platform_handle_dispatcher.h index 701d6ef..99ffcac 100644 --- a/mojo/edk/system/platform_handle_dispatcher.h +++ b/mojo/edk/system/platform_handle_dispatcher.h @@ -8,7 +8,7 @@ #include "mojo/edk/embedder/scoped_platform_handle.h" #include "mojo/edk/system/simple_dispatcher.h" #include "mojo/edk/system/system_impl_export.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/raw_channel.h b/mojo/edk/system/raw_channel.h index f6ff1af..07b7881 100644 --- a/mojo/edk/system/raw_channel.h +++ b/mojo/edk/system/raw_channel.h @@ -15,7 +15,7 @@ #include "mojo/edk/system/message_in_transit.h" #include "mojo/edk/system/message_in_transit_queue.h" #include "mojo/edk/system/system_impl_export.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/raw_channel_posix.cc b/mojo/edk/system/raw_channel_posix.cc index 9f68d06..e6627d6 100644 --- a/mojo/edk/system/raw_channel_posix.cc +++ b/mojo/edk/system/raw_channel_posix.cc @@ -26,7 +26,7 @@ #include "mojo/edk/embedder/platform_handle.h" #include "mojo/edk/embedder/platform_handle_vector.h" #include "mojo/edk/system/transport_data.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/system/macros.h" #if !defined(SO_PEEK_OFF) #define SO_PEEK_OFF 42 diff --git a/mojo/edk/system/raw_channel_unittest.cc b/mojo/edk/system/raw_channel_unittest.cc index d9be055..fdc77d8 100644 --- a/mojo/edk/system/raw_channel_unittest.cc +++ b/mojo/edk/system/raw_channel_unittest.cc @@ -31,8 +31,8 @@ #include "mojo/edk/system/test_utils.h" #include "mojo/edk/system/transport_data.h" #include "mojo/edk/test/test_utils.h" +#include "mojo/public/cpp/system/macros.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/raw_channel_win.cc b/mojo/edk/system/raw_channel_win.cc index ec31501..3063ebe 100644 --- a/mojo/edk/system/raw_channel_win.cc +++ b/mojo/edk/system/raw_channel_win.cc @@ -19,7 +19,7 @@ #include "mojo/edk/embedder/embedder_internal.h" #include "mojo/edk/embedder/platform_handle.h" #include "mojo/edk/system/transport_data.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/system/macros.h" #define STATUS_CANCELLED 0xC0000120 #define STATUS_PIPE_BROKEN 0xC000014B diff --git a/mojo/edk/system/shared_buffer_dispatcher.cc b/mojo/edk/system/shared_buffer_dispatcher.cc index 1f373d8..1f15ea2 100644 --- a/mojo/edk/system/shared_buffer_dispatcher.cc +++ b/mojo/edk/system/shared_buffer_dispatcher.cc @@ -12,7 +12,7 @@ #include "mojo/edk/embedder/platform_support.h" #include "mojo/edk/system/configuration.h" #include "mojo/edk/system/options_validation.h" -#include "third_party/mojo/src/mojo/public/c/system/macros.h" +#include "mojo/public/c/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/shared_buffer_dispatcher.h b/mojo/edk/system/shared_buffer_dispatcher.h index 5c7cbb7..f3fc9e8 100644 --- a/mojo/edk/system/shared_buffer_dispatcher.h +++ b/mojo/edk/system/shared_buffer_dispatcher.h @@ -8,7 +8,7 @@ #include "mojo/edk/embedder/platform_shared_buffer.h" #include "mojo/edk/system/simple_dispatcher.h" #include "mojo/edk/system/system_impl_export.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { diff --git a/mojo/edk/system/shared_buffer_dispatcher_unittest.cc b/mojo/edk/system/shared_buffer_dispatcher_unittest.cc index bcbc49e..ceb936b 100644 --- a/mojo/edk/system/shared_buffer_dispatcher_unittest.cc +++ b/mojo/edk/system/shared_buffer_dispatcher_unittest.cc @@ -10,8 +10,8 @@ #include "mojo/edk/embedder/platform_shared_buffer.h" #include "mojo/edk/embedder/simple_platform_support.h" #include "mojo/edk/system/dispatcher.h" +#include "mojo/public/cpp/system/macros.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/simple_dispatcher.h b/mojo/edk/system/simple_dispatcher.h index 705c5a1..38e9d54 100644 --- a/mojo/edk/system/simple_dispatcher.h +++ b/mojo/edk/system/simple_dispatcher.h @@ -10,7 +10,7 @@ #include "mojo/edk/system/awakable_list.h" #include "mojo/edk/system/dispatcher.h" #include "mojo/edk/system/system_impl_export.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/simple_dispatcher_unittest.cc b/mojo/edk/system/simple_dispatcher_unittest.cc index 6f85496..b7f7d953 100644 --- a/mojo/edk/system/simple_dispatcher_unittest.cc +++ b/mojo/edk/system/simple_dispatcher_unittest.cc @@ -16,8 +16,8 @@ #include "mojo/edk/system/test_utils.h" #include "mojo/edk/system/waiter.h" #include "mojo/edk/system/waiter_test_utils.h" +#include "mojo/public/cpp/system/macros.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/test_utils.h b/mojo/edk/system/test_utils.h index 2baff3c..cf244b3 100644 --- a/mojo/edk/system/test_utils.h +++ b/mojo/edk/system/test_utils.h @@ -8,9 +8,9 @@ #include "base/test/test_io_thread.h" #include "base/time/time.h" #include "mojo/edk/test/scoped_ipc_support.h" +#include "mojo/public/c/system/types.h" +#include "mojo/public/cpp/system/macros.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/mojo/src/mojo/public/c/system/types.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/transport_data.h b/mojo/edk/system/transport_data.h index bd56463..16d8d01 100644 --- a/mojo/edk/system/transport_data.h +++ b/mojo/edk/system/transport_data.h @@ -16,7 +16,7 @@ #include "mojo/edk/embedder/platform_handle_vector.h" #include "mojo/edk/system/dispatcher.h" #include "mojo/edk/system/system_impl_export.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/waiter.h b/mojo/edk/system/waiter.h index 541e9c1..fa8e836 100644 --- a/mojo/edk/system/waiter.h +++ b/mojo/edk/system/waiter.h @@ -11,8 +11,8 @@ #include "base/synchronization/lock.h" #include "mojo/edk/system/awakable.h" #include "mojo/edk/system/system_impl_export.h" -#include "third_party/mojo/src/mojo/public/c/system/types.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/c/system/types.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/waiter_test_utils.h b/mojo/edk/system/waiter_test_utils.h index ca2158a..97434d5 100644 --- a/mojo/edk/system/waiter_test_utils.h +++ b/mojo/edk/system/waiter_test_utils.h @@ -12,8 +12,8 @@ #include "mojo/edk/system/dispatcher.h" #include "mojo/edk/system/handle_signals_state.h" #include "mojo/edk/system/waiter.h" -#include "third_party/mojo/src/mojo/public/c/system/types.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/c/system/types.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/system/waiter_unittest.cc b/mojo/edk/system/waiter_unittest.cc index ed6be9a..3e3827d 100644 --- a/mojo/edk/system/waiter_unittest.cc +++ b/mojo/edk/system/waiter_unittest.cc @@ -14,8 +14,8 @@ #include "base/synchronization/lock.h" #include "base/threading/simple_thread.h" #include "mojo/edk/system/test_utils.h" +#include "mojo/public/cpp/system/macros.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/test/BUILD.gn b/mojo/edk/test/BUILD.gn index 2f5fff5..5f8ec77 100644 --- a/mojo/edk/test/BUILD.gn +++ b/mojo/edk/test/BUILD.gn @@ -3,7 +3,6 @@ # found in the LICENSE file. import("//mojo/edk/mojo_edk.gni") -import("//mojo/public/mojo.gni") import("//testing/test.gni") mojo_edk_source_set("test_support") { @@ -21,8 +20,8 @@ mojo_edk_source_set("test_support") { deps = [ "//base", "//base/test:test_support", + "//mojo/public/cpp/system", "//testing/gtest", - "//third_party/mojo/src/mojo/public/cpp/system", # TODO(use_chrome_edk): temporary since the Mojo wrapper primitives are # declared in third party only for now. @@ -41,8 +40,8 @@ mojo_edk_source_set("run_all_unittests") { ":test_support_impl", "//base", "//base/test:test_support", + "//mojo/public/c/test_support", "//testing/gtest", - "//third_party/mojo/src/mojo/public/c/test_support", # TODO(use_chrome_edk): temporary since the Mojo wrapper primitives are # declared in third party only for now. @@ -56,11 +55,11 @@ mojo_edk_source_set("run_all_perftests") { ":test_support_impl", "//base", "//base/test:test_support", + "//mojo/public/c/test_support", # TODO(use_chrome_edk): temporary since the Mojo wrapper primitives are # declared in third party only for now. "//third_party/mojo/src/mojo/edk/system", - "//third_party/mojo/src/mojo/public/c/test_support", ] sources = [ @@ -73,8 +72,8 @@ mojo_edk_source_set("test_support_impl") { deps = [ "//base", "//base/test:test_support", - "//third_party/mojo/src/mojo/public/c/test_support", - "//third_party/mojo/src/mojo/public/cpp/system", + "//mojo/public/c/test_support", + "//mojo/public/cpp/system", ] sources = [ @@ -98,27 +97,13 @@ group("public_tests") { ":mojo_public_system_unittests2", ":mojo_public_utility_unittests2", ] - - if (mojo_use_application_in_sdk) { - deps += [ ":mojo_public_application_unittests" ] - } -} - -if (mojo_use_application_in_sdk) { - # TODO(use_chrome_edk): remove "2" - test("mojo_public_application_unittests2") { - deps = [ - ":run_all_unittests", - "../../../third_party/mojo/src/mojo/public/cpp/application/tests", - ] - } } # TODO(use_chrome_edk): remove "2" test("mojo_public_bindings_unittests2") { deps = [ ":run_all_unittests", - "../../../third_party/mojo/src/mojo/public/cpp/bindings/tests", + "//mojo/public/cpp/bindings/tests", ] } @@ -126,7 +111,7 @@ test("mojo_public_bindings_unittests2") { test("mojo_public_environment_unittests2") { deps = [ ":run_all_unittests", - "../../../third_party/mojo/src/mojo/public/cpp/environment/tests", + "//mojo/public/cpp/environment/tests", ] } @@ -134,7 +119,7 @@ test("mojo_public_environment_unittests2") { test("mojo_public_system_perftests2") { deps = [ ":run_all_perftests", - "../../../third_party/mojo/src/mojo/public/c/system/tests:perftests", + "//mojo/public/c/system/tests:perftests", ] } @@ -142,7 +127,7 @@ test("mojo_public_system_perftests2") { test("mojo_public_system_unittests2") { deps = [ ":run_all_unittests", - "../../../third_party/mojo/src/mojo/public/cpp/system/tests", + "//mojo/public/cpp/system/tests", ] } @@ -150,6 +135,6 @@ test("mojo_public_system_unittests2") { test("mojo_public_utility_unittests2") { deps = [ ":run_all_unittests", - "../../../third_party/mojo/src/mojo/public/cpp/utility/tests", + "//mojo/public/cpp/utility/tests", ] } diff --git a/mojo/edk/test/multiprocess_test_helper.h b/mojo/edk/test/multiprocess_test_helper.h index 14a4035..3ff8b85 100644 --- a/mojo/edk/test/multiprocess_test_helper.h +++ b/mojo/edk/test/multiprocess_test_helper.h @@ -11,8 +11,8 @@ #include "base/test/multiprocess_test.h" #include "base/test/test_timeouts.h" #include "mojo/edk/embedder/scoped_platform_handle.h" +#include "mojo/public/cpp/system/macros.h" #include "testing/multiprocess_func_list.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" namespace mojo { diff --git a/mojo/edk/test/run_all_perftests.cc b/mojo/edk/test/run_all_perftests.cc index c9692ef..d700bc3 100644 --- a/mojo/edk/test/run_all_perftests.cc +++ b/mojo/edk/test/run_all_perftests.cc @@ -6,7 +6,7 @@ #include "base/test/perf_test_suite.h" #include "mojo/edk/embedder/embedder.h" #include "mojo/edk/test/test_support_impl.h" -#include "third_party/mojo/src/mojo/public/tests/test_support_private.h" +#include "mojo/public/tests/test_support_private.h" int main(int argc, char** argv) { mojo::edk::Init(); diff --git a/mojo/edk/test/run_all_unittests.cc b/mojo/edk/test/run_all_unittests.cc index 26744ee..23266444 100644 --- a/mojo/edk/test/run_all_unittests.cc +++ b/mojo/edk/test/run_all_unittests.cc @@ -11,8 +11,8 @@ #include "mojo/edk/embedder/embedder.h" #include "mojo/edk/test/scoped_ipc_support.h" #include "mojo/edk/test/test_support_impl.h" +#include "mojo/public/tests/test_support_private.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/mojo/src/mojo/public/tests/test_support_private.h" int main(int argc, char** argv) { #if !defined(OS_ANDROID) diff --git a/mojo/edk/test/scoped_ipc_support.cc b/mojo/edk/test/scoped_ipc_support.cc index c252529..c416875 100644 --- a/mojo/edk/test/scoped_ipc_support.cc +++ b/mojo/edk/test/scoped_ipc_support.cc @@ -32,8 +32,7 @@ void ScopedIPCSupportHelper::Init( scoped_refptr<base::TaskRunner> io_thread_task_runner) { io_thread_task_runner_ = io_thread_task_runner; // Note: Run delegate methods on the I/O thread. - InitIPCSupport(io_thread_task_runner_, process_delegate, - io_thread_task_runner_); + InitIPCSupport(process_delegate, io_thread_task_runner_); } void ScopedIPCSupportHelper::OnShutdownCompleteImpl() { diff --git a/mojo/edk/test/scoped_ipc_support.h b/mojo/edk/test/scoped_ipc_support.h index 2bb5ff7..cff0da7 100644 --- a/mojo/edk/test/scoped_ipc_support.h +++ b/mojo/edk/test/scoped_ipc_support.h @@ -11,7 +11,7 @@ #include "base/task_runner.h" #include "mojo/edk/embedder/process_delegate.h" #include "mojo/edk/embedder/scoped_platform_handle.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { namespace edk { diff --git a/mojo/edk/test/test_support_impl.h b/mojo/edk/test/test_support_impl.h index 85e640e..e672999 100644 --- a/mojo/edk/test/test_support_impl.h +++ b/mojo/edk/test/test_support_impl.h @@ -7,8 +7,8 @@ #include <stdio.h> -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" -#include "third_party/mojo/src/mojo/public/tests/test_support_private.h" +#include "mojo/public/cpp/system/macros.h" +#include "mojo/public/tests/test_support_private.h" namespace mojo { namespace edk { diff --git a/mojo/environment/BUILD.gn b/mojo/environment/BUILD.gn index f376bac..d794af7 100644 --- a/mojo/environment/BUILD.gn +++ b/mojo/environment/BUILD.gn @@ -10,13 +10,13 @@ source_set("chromium") { "environment.cc", # TODO(vtl): This is kind of ugly. (See TODO in logging.h.) - "//third_party/mojo/src/mojo/public/cpp/environment/async_waiter.h", - "//third_party/mojo/src/mojo/public/cpp/environment/lib/async_waiter.cc", - "//third_party/mojo/src/mojo/public/cpp/environment/lib/logging.cc", - "//third_party/mojo/src/mojo/public/cpp/environment/lib/scoped_task_tracking.cc", - "//third_party/mojo/src/mojo/public/cpp/environment/lib/scoped_task_tracking.h", - "//third_party/mojo/src/mojo/public/cpp/environment/logging.h", - "//third_party/mojo/src/mojo/public/cpp/environment/task_tracker.h", + "//mojo/public/cpp/environment/async_waiter.h", + "//mojo/public/cpp/environment/lib/async_waiter.cc", + "//mojo/public/cpp/environment/lib/logging.cc", + "//mojo/public/cpp/environment/lib/scoped_task_tracking.cc", + "//mojo/public/cpp/environment/lib/scoped_task_tracking.h", + "//mojo/public/cpp/environment/logging.h", + "//mojo/public/cpp/environment/task_tracker.h", ] public_deps = [ @@ -24,10 +24,10 @@ source_set("chromium") { ] deps = [ - "//third_party/mojo/src/mojo/public/c/environment", - "//third_party/mojo/src/mojo/public/cpp/bindings:callback", - "//third_party/mojo/src/mojo/public/cpp/environment", - "//third_party/mojo/src/mojo/public/cpp/system", + "//mojo/public/c/environment", + "//mojo/public/cpp/bindings:callback", + "//mojo/public/cpp/environment", + "//mojo/public/cpp/system", ] } @@ -53,7 +53,7 @@ component("chromium_impl") { "//base", "//base/third_party/dynamic_annotations", "//mojo/message_pump", - "//third_party/mojo/src/mojo/public/c/system:for_component", - "//third_party/mojo/src/mojo/public/cpp/environment:environment", + "//mojo/public/c/system:for_component", + "//mojo/public/cpp/environment:environment", ] } diff --git a/mojo/environment/default_async_waiter_impl.h b/mojo/environment/default_async_waiter_impl.h index dea4621..5140e35 100644 --- a/mojo/environment/default_async_waiter_impl.h +++ b/mojo/environment/default_async_waiter_impl.h @@ -6,7 +6,7 @@ #define MOJO_ENVIRONMENT_DEFAULT_ASYNC_WAITER_IMPL_H_ #include "mojo/environment/mojo_environment_impl_export.h" -#include "third_party/mojo/src/mojo/public/c/environment/async_waiter.h" +#include "mojo/public/c/environment/async_waiter.h" namespace mojo { namespace internal { diff --git a/mojo/environment/default_logger_impl.h b/mojo/environment/default_logger_impl.h index ca73237..c87a727 100644 --- a/mojo/environment/default_logger_impl.h +++ b/mojo/environment/default_logger_impl.h @@ -6,7 +6,7 @@ #define MOJO_ENVIRONMENT_DEFAULT_LOGGER_IMPL_H_ #include "mojo/environment/mojo_environment_impl_export.h" -#include "third_party/mojo/src/mojo/public/c/environment/logger.h" +#include "mojo/public/c/environment/logger.h" namespace mojo { namespace internal { diff --git a/mojo/environment/default_task_tracker_impl.h b/mojo/environment/default_task_tracker_impl.h index 0a417cf..34d7f04 100644 --- a/mojo/environment/default_task_tracker_impl.h +++ b/mojo/environment/default_task_tracker_impl.h @@ -6,7 +6,7 @@ #define MOJO_ENVIRONMENT_DEFAULT_TASK_TRACKER_IMPL_H_ #include "mojo/environment/mojo_environment_impl_export.h" -#include "third_party/mojo/src/mojo/public/cpp/environment/task_tracker.h" +#include "mojo/public/cpp/environment/task_tracker.h" namespace mojo { namespace internal { diff --git a/mojo/environment/environment.cc b/mojo/environment/environment.cc index 7cc9b1c..7b342e0 100644 --- a/mojo/environment/environment.cc +++ b/mojo/environment/environment.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "third_party/mojo/src/mojo/public/cpp/environment/environment.h" +#include "mojo/public/cpp/environment/environment.h" #include "mojo/environment/default_async_waiter_impl.h" #include "mojo/environment/default_logger_impl.h" diff --git a/mojo/fetcher/BUILD.gn b/mojo/fetcher/BUILD.gn index da681ca..358174c 100644 --- a/mojo/fetcher/BUILD.gn +++ b/mojo/fetcher/BUILD.gn @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import("//third_party/mojo/src/mojo/public/tools/bindings/mojom.gni") +import("//mojo/public/tools/bindings/mojom.gni") source_set("fetcher") { sources = [ @@ -26,9 +26,9 @@ source_set("fetcher") { "//base", "//mojo/application/public/interfaces", "//mojo/common", + "//mojo/public/cpp/bindings", "//mojo/services/network/public/interfaces", "//mojo/services/updater", - "//third_party/mojo/src/mojo/public/cpp/bindings", "//url", ] deps = [ diff --git a/mojo/fetcher/data_fetcher.cc b/mojo/fetcher/data_fetcher.cc index 51c25a8..d5f875e 100644 --- a/mojo/fetcher/data_fetcher.cc +++ b/mojo/fetcher/data_fetcher.cc @@ -10,8 +10,8 @@ #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/thread_task_runner_handle.h" +#include "mojo/public/cpp/system/data_pipe.h" #include "net/base/data_url.h" -#include "third_party/mojo/src/mojo/public/cpp/system/data_pipe.h" namespace mojo { namespace fetcher { diff --git a/mojo/fetcher/data_fetcher_unittest.cc b/mojo/fetcher/data_fetcher_unittest.cc index b161230..cfaeb84 100644 --- a/mojo/fetcher/data_fetcher_unittest.cc +++ b/mojo/fetcher/data_fetcher_unittest.cc @@ -11,8 +11,8 @@ #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" +#include "mojo/public/cpp/system/data_pipe.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/mojo/src/mojo/public/cpp/system/data_pipe.h" namespace mojo { namespace fetcher { diff --git a/mojo/fetcher/network_fetcher_unittest.cc b/mojo/fetcher/network_fetcher_unittest.cc index cd6c8d932..e9583e9 100644 --- a/mojo/fetcher/network_fetcher_unittest.cc +++ b/mojo/fetcher/network_fetcher_unittest.cc @@ -11,10 +11,10 @@ #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "mojo/fetcher/network_fetcher.h" +#include "mojo/public/cpp/bindings/strong_binding.h" #include "mojo/services/network/public/interfaces/url_loader.mojom.h" #include "mojo/services/network/public/interfaces/url_loader_factory.mojom.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h" namespace mojo { namespace fetcher { diff --git a/mojo/gles2/BUILD.gn b/mojo/gles2/BUILD.gn index 39912e3..de8dda2 100644 --- a/mojo/gles2/BUILD.gn +++ b/mojo/gles2/BUILD.gn @@ -22,8 +22,8 @@ source_set("headers") { "//gpu/command_buffer/client", "//gpu/command_buffer/client:gles2_implementation", "//gpu/command_buffer/common", - "//third_party/mojo/src/mojo/public/c/gles2", - "//third_party/mojo/src/mojo/public/cpp/bindings", + "//mojo/public/c/gles2", + "//mojo/public/cpp/bindings", ] } @@ -61,10 +61,10 @@ component("gles2") { "//gpu/command_buffer/common", "//mojo/environment:chromium", "//mojo/platform_handle:for_component", - "//third_party/mojo/src/mojo/public/c/gles2", - "//third_party/mojo/src/mojo/public/c/system:for_component", - "//third_party/mojo/src/mojo/public/cpp/bindings", - "//third_party/mojo/src/mojo/public/cpp/system", + "//mojo/public/c/gles2", + "//mojo/public/c/system:for_component", + "//mojo/public/cpp/bindings", + "//mojo/public/cpp/system", ] if (!is_component_build) { diff --git a/mojo/gles2/command_buffer_client_impl.h b/mojo/gles2/command_buffer_client_impl.h index d04e73d..b0ce570 100644 --- a/mojo/gles2/command_buffer_client_impl.h +++ b/mojo/gles2/command_buffer_client_impl.h @@ -14,7 +14,7 @@ #include "gpu/command_buffer/client/gpu_control.h" #include "gpu/command_buffer/common/command_buffer.h" #include "gpu/command_buffer/common/command_buffer_shared.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/binding.h" namespace base { class RunLoop; diff --git a/mojo/gles2/gles2_context.cc b/mojo/gles2/gles2_context.cc index d16bc42..5574840 100644 --- a/mojo/gles2/gles2_context.cc +++ b/mojo/gles2/gles2_context.cc @@ -7,8 +7,8 @@ #include "gpu/command_buffer/client/gles2_cmd_helper.h" #include "gpu/command_buffer/client/gles2_implementation.h" #include "gpu/command_buffer/client/transfer_buffer.h" -#include "third_party/mojo/src/mojo/public/c/gles2/gles2.h" -#include "third_party/mojo/src/mojo/public/cpp/system/core.h" +#include "mojo/public/c/gles2/gles2.h" +#include "mojo/public/cpp/system/core.h" namespace gles2 { diff --git a/mojo/gles2/gles2_context.h b/mojo/gles2/gles2_context.h index 829b324..5ab4b844 100644 --- a/mojo/gles2/gles2_context.h +++ b/mojo/gles2/gles2_context.h @@ -11,7 +11,7 @@ #include "base/memory/scoped_ptr.h" #include "gpu/command_buffer/client/gles2_implementation.h" #include "mojo/gles2/command_buffer_client_impl.h" -#include "third_party/mojo/src/mojo/public/c/gles2/gles2.h" +#include "mojo/public/c/gles2/gles2.h" struct MojoGLES2ContextPrivate {}; diff --git a/mojo/gles2/gles2_impl.cc b/mojo/gles2/gles2_impl.cc index f106977..9e4fb9a 100644 --- a/mojo/gles2/gles2_impl.cc +++ b/mojo/gles2/gles2_impl.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "third_party/mojo/src/mojo/public/c/gles2/gles2.h" +#include "mojo/public/c/gles2/gles2.h" #include "base/lazy_instance.h" #include "base/threading/thread_local.h" @@ -11,7 +11,7 @@ #include "mojo/gles2/gles2_context.h" // Even though this isn't used here, we need to include it to get the symbols to // be exported in component build. -#include "third_party/mojo/src/mojo/public/c/gles2/chromium_extension.h" +#include "mojo/public/c/gles2/chromium_extension.h" using gles2::GLES2Context; @@ -94,8 +94,8 @@ void* MojoGLES2GetContextSupport(MojoGLES2Context context) { DCHECK(g_gpu_interface.Get().Get()); \ return g_gpu_interface.Get().Get()->Function ARGUMENTS; \ } -#include "third_party/mojo/src/mojo/public/c/gles2/gles2_call_visitor_autogen.h" -#include "third_party/mojo/src/mojo/public/c/gles2/gles2_call_visitor_chromium_extension_autogen.h" +#include "mojo/public/c/gles2/gles2_call_visitor_autogen.h" +#include "mojo/public/c/gles2/gles2_call_visitor_chromium_extension_autogen.h" #undef VISIT_GL_CALL } // extern "C" diff --git a/mojo/gpu/BUILD.gn b/mojo/gpu/BUILD.gn index 4c5e350..ad37238 100644 --- a/mojo/gpu/BUILD.gn +++ b/mojo/gpu/BUILD.gn @@ -13,6 +13,6 @@ source_set("mojo_gles2_implementation") { deps = [ "//base", "//gpu/command_buffer/client:gles2_interface", - "//third_party/mojo/src/mojo/public/c/gles2", + "//mojo/public/c/gles2", ] } diff --git a/mojo/gpu/mojo_gles2_impl_autogen.cc b/mojo/gpu/mojo_gles2_impl_autogen.cc index e6386ea..2413ba8 100644 --- a/mojo/gpu/mojo_gles2_impl_autogen.cc +++ b/mojo/gpu/mojo_gles2_impl_autogen.cc @@ -11,8 +11,8 @@ #include "mojo/gpu/mojo_gles2_impl_autogen.h" #include "base/logging.h" -#include "third_party/mojo/src/mojo/public/c/gles2/chromium_extension.h" -#include "third_party/mojo/src/mojo/public/c/gles2/gles2.h" +#include "mojo/public/c/gles2/chromium_extension.h" +#include "mojo/public/c/gles2/gles2.h" namespace mojo { diff --git a/mojo/gpu/mojo_gles2_impl_autogen.h b/mojo/gpu/mojo_gles2_impl_autogen.h index b2c1867..c0b1c86 100644 --- a/mojo/gpu/mojo_gles2_impl_autogen.h +++ b/mojo/gpu/mojo_gles2_impl_autogen.h @@ -14,7 +14,7 @@ #define MOJO_GPU_MOJO_GLES2_IMPL_AUTOGEN_H_ #include "gpu/command_buffer/client/gles2_interface.h" -#include "third_party/mojo/src/mojo/public/c/gles2/gles2.h" +#include "mojo/public/c/gles2/gles2.h" namespace mojo { diff --git a/mojo/message_pump/BUILD.gn b/mojo/message_pump/BUILD.gn index b8132a1..3f1298b 100644 --- a/mojo/message_pump/BUILD.gn +++ b/mojo/message_pump/BUILD.gn @@ -17,10 +17,13 @@ component("message_pump") { defines = [ "MOJO_MESSAGE_PUMP_IMPLEMENTATION" ] - deps = [ + public_deps = [ "//base", + "//mojo/public/cpp/system", + ] + + deps = [ "//base/third_party/dynamic_annotations", - "//third_party/mojo/src/mojo/public/c/system:for_component", - "//third_party/mojo/src/mojo/public/cpp/system", + "//mojo/public/c/system:for_component", ] } diff --git a/mojo/message_pump/handle_watcher.h b/mojo/message_pump/handle_watcher.h index 4f7115a..1dc079e 100644 --- a/mojo/message_pump/handle_watcher.h +++ b/mojo/message_pump/handle_watcher.h @@ -10,7 +10,7 @@ #include "base/memory/scoped_ptr.h" #include "base/run_loop.h" #include "mojo/message_pump/mojo_message_pump_export.h" -#include "third_party/mojo/src/mojo/public/cpp/system/core.h" +#include "mojo/public/cpp/system/core.h" namespace base { class Thread; diff --git a/mojo/message_pump/handle_watcher_perftest.cc b/mojo/message_pump/handle_watcher_perftest.cc index afdbb9f..4ea4f26 100644 --- a/mojo/message_pump/handle_watcher_perftest.cc +++ b/mojo/message_pump/handle_watcher_perftest.cc @@ -12,9 +12,9 @@ #include "base/run_loop.h" #include "base/time/time.h" #include "mojo/message_pump/message_pump_mojo.h" +#include "mojo/public/cpp/test_support/test_support.h" +#include "mojo/public/cpp/test_support/test_utils.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/mojo/src/mojo/public/cpp/test_support/test_support.h" -#include "third_party/mojo/src/mojo/public/cpp/test_support/test_utils.h" namespace mojo { namespace common { diff --git a/mojo/message_pump/handle_watcher_unittest.cc b/mojo/message_pump/handle_watcher_unittest.cc index bfbfa9f..f904df05 100644 --- a/mojo/message_pump/handle_watcher_unittest.cc +++ b/mojo/message_pump/handle_watcher_unittest.cc @@ -15,9 +15,9 @@ #include "base/threading/thread.h" #include "mojo/message_pump/message_pump_mojo.h" #include "mojo/message_pump/time_helper.h" +#include "mojo/public/cpp/system/core.h" +#include "mojo/public/cpp/test_support/test_utils.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/mojo/src/mojo/public/cpp/system/core.h" -#include "third_party/mojo/src/mojo/public/cpp/test_support/test_utils.h" namespace mojo { namespace common { diff --git a/mojo/message_pump/message_pump_mojo.h b/mojo/message_pump/message_pump_mojo.h index d4028f1..0686973 100644 --- a/mojo/message_pump/message_pump_mojo.h +++ b/mojo/message_pump/message_pump_mojo.h @@ -14,7 +14,7 @@ #include "base/synchronization/lock.h" #include "base/time/time.h" #include "mojo/message_pump/mojo_message_pump_export.h" -#include "third_party/mojo/src/mojo/public/cpp/system/core.h" +#include "mojo/public/cpp/system/core.h" namespace mojo { namespace common { diff --git a/mojo/message_pump/message_pump_mojo_handler.h b/mojo/message_pump/message_pump_mojo_handler.h index 35ae036..8beb9ba 100644 --- a/mojo/message_pump/message_pump_mojo_handler.h +++ b/mojo/message_pump/message_pump_mojo_handler.h @@ -6,7 +6,7 @@ #define MOJO_MESSAGE_PUMP_MESSAGE_PUMP_MOJO_HANDLER_H_ #include "mojo/message_pump/mojo_message_pump_export.h" -#include "third_party/mojo/src/mojo/public/cpp/system/core.h" +#include "mojo/public/cpp/system/core.h" namespace mojo { namespace common { diff --git a/mojo/message_pump/message_pump_mojo_unittest.cc b/mojo/message_pump/message_pump_mojo_unittest.cc index 51f3670..687d5f3 100644 --- a/mojo/message_pump/message_pump_mojo_unittest.cc +++ b/mojo/message_pump/message_pump_mojo_unittest.cc @@ -7,8 +7,8 @@ #include "base/message_loop/message_loop_test.h" #include "base/run_loop.h" #include "mojo/message_pump/message_pump_mojo_handler.h" +#include "mojo/public/cpp/system/core.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/mojo/src/mojo/public/cpp/system/core.h" namespace mojo { namespace common { diff --git a/mojo/mojo_base.gyp b/mojo/mojo_base.gyp index 44879eb..9c308c6 100644 --- a/mojo/mojo_base.gyp +++ b/mojo/mojo_base.gyp @@ -156,13 +156,13 @@ ], 'sources': [ # TODO(vtl): This is kind of ugly. (See TODO in logging.h.) - "../third_party/mojo/src/mojo/public/cpp/environment/async_waiter.h", - "../third_party/mojo/src/mojo/public/cpp/environment/lib/async_waiter.cc", - "../third_party/mojo/src/mojo/public/cpp/environment/lib/logging.cc", - "../third_party/mojo/src/mojo/public/cpp/environment/lib/scoped_task_tracking.cc", - "../third_party/mojo/src/mojo/public/cpp/environment/lib/scoped_task_tracking.cc", - "../third_party/mojo/src/mojo/public/cpp/environment/logging.h", - "../third_party/mojo/src/mojo/public/cpp/environment/task_tracker.h", + "../mojo/public/cpp/environment/async_waiter.h", + "../mojo/public/cpp/environment/lib/async_waiter.cc", + "../mojo/public/cpp/environment/lib/logging.cc", + "../mojo/public/cpp/environment/lib/scoped_task_tracking.cc", + "../mojo/public/cpp/environment/lib/scoped_task_tracking.cc", + "../mojo/public/cpp/environment/logging.h", + "../mojo/public/cpp/environment/task_tracker.h", 'environment/environment.cc', ], 'include_dirs': [ diff --git a/mojo/mojo_public_gles2_for_loadable_module.gypi b/mojo/mojo_public_gles2_for_loadable_module.gypi deleted file mode 100644 index cd5dbbf..0000000 --- a/mojo/mojo_public_gles2_for_loadable_module.gypi +++ /dev/null @@ -1,48 +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. - -# In the non component build, the thunks need to be linked directly into the -# loadable module since they define symbols that should be exported from that -# library. So, this variable expands out to either list the sources directly (in -# the component build where no symbols need to be exported) a dependency. -{ - 'conditions': [ - ['component=="shared_library"', { - 'dependencies': [ - '<(DEPTH)/mojo/mojo.gyp:mojo_gles2_impl', - ], - }, { # component!="shared_library" - 'defines': [ - 'MOJO_GLES2_IMPLEMENTATION', - 'GLES2_USE_MOJO', - ], - 'include_dirs': [ - '<(DEPTH)', - ], - 'dependencies': [ - '<(DEPTH)/third_party/khronos/khronos.gyp:khronos_headers' - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '<(DEPTH)', - ], - 'defines': [ - 'GLES2_USE_MOJO', - ], - }, - 'sources': [ - '<(DEPTH)/third_party/mojo/src/mojo/public/c/gles2/gles2.h', - '<(DEPTH)/third_party/mojo/src/mojo/public/c/gles2/gles2_export.h', - '<(DEPTH)/third_party/mojo/src/mojo/public/platform/native/gles2_thunks.cc', - '<(DEPTH)/third_party/mojo/src/mojo/public/platform/native/gles2_thunks.h', - '<(DEPTH)/third_party/mojo/src/mojo/public/platform/native/gles2_impl_thunks.cc', - '<(DEPTH)/third_party/mojo/src/mojo/public/platform/native/gles2_impl_thunks.h', - '<(DEPTH)/third_party/mojo/src/mojo/public/platform/native/gles2_impl_chromium_texture_mailbox_thunks.cc', - '<(DEPTH)/third_party/mojo/src/mojo/public/platform/native/gles2_impl_chromium_texture_mailbox_thunks.h', - '<(DEPTH)/third_party/mojo/src/mojo/public/platform/native/gles2_impl_chromium_sync_point_thunks.cc', - '<(DEPTH)/third_party/mojo/src/mojo/public/platform/native/gles2_impl_chromium_sync_point_thunks.h', - ], - }] - ] -} diff --git a/mojo/package_manager/content_handler_unittest.cc b/mojo/package_manager/content_handler_unittest.cc index 0b1dca2..dfce5fc 100644 --- a/mojo/package_manager/content_handler_unittest.cc +++ b/mojo/package_manager/content_handler_unittest.cc @@ -16,13 +16,13 @@ #include "mojo/application/public/interfaces/content_handler.mojom.h" #include "mojo/application/public/interfaces/service_provider.mojom.h" #include "mojo/package_manager/package_manager_impl.h" +#include "mojo/public/cpp/bindings/strong_binding.h" #include "mojo/shell/application_loader.h" #include "mojo/shell/application_manager.h" #include "mojo/shell/connect_util.h" #include "mojo/shell/fetcher.h" #include "mojo/shell/test_package_manager.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h" namespace mojo { namespace package_manager { diff --git a/mojo/platform_handle/BUILD.gn b/mojo/platform_handle/BUILD.gn index 2afc90a..d5818f7 100644 --- a/mojo/platform_handle/BUILD.gn +++ b/mojo/platform_handle/BUILD.gn @@ -2,8 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -# See comments in third_party/mojo/src/mojo/public/c/system/BUILD.gn which this -# is based on. +# See comments in mojo/public/c/system/BUILD.gn which this is based on. # Summary: depend on this target to use the platform handle functions without # linking against a specific implementation. # For component targets, add //mojo/platform_handle:for_component to your deps @@ -16,7 +15,7 @@ source_set("platform_handle") { "platform_handle_functions.h", ] deps = [ - "//third_party/mojo/src/mojo/public/c/system", + "//mojo/public/c/system", ] } @@ -31,8 +30,8 @@ component("platform_handle_impl") { defines = [ "PLATFORM_HANDLE_IMPLEMENTATION" ] deps = [ "//base", + "//mojo/public/c/system:for_component", "//third_party/mojo/src/mojo/edk/embedder:headers", - "//third_party/mojo/src/mojo/public/c/system:for_component", ] } @@ -46,7 +45,7 @@ source_set("platform_handle_thunks") { ] defines = [ "PLATFORM_HANDLE_IMPLEMENTATION" ] deps = [ - "//third_party/mojo/src/mojo/public/c/system", + "//mojo/public/c/system", ] } diff --git a/mojo/platform_handle/platform_handle_functions.h b/mojo/platform_handle/platform_handle_functions.h index dec5d1f..6b9276b 100644 --- a/mojo/platform_handle/platform_handle_functions.h +++ b/mojo/platform_handle/platform_handle_functions.h @@ -7,7 +7,7 @@ #include "mojo/platform_handle/platform_handle.h" #include "mojo/platform_handle/platform_handle_exports.h" -#include "third_party/mojo/src/mojo/public/c/system/types.h" +#include "mojo/public/c/system/types.h" extern "C" { diff --git a/mojo/public/BUILD.gn b/mojo/public/BUILD.gn new file mode 100644 index 0000000..d7bb448 --- /dev/null +++ b/mojo/public/BUILD.gn @@ -0,0 +1,41 @@ +# 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. + +group("public") { + # Meta-target, don't link into production code. + testonly = true + deps = [ + ":libmojo_sdk", + ":sdk", + "cpp/bindings", + "cpp/environment:standalone", + "cpp/utility", + "interfaces/bindings/tests:test_interfaces", + ] + + if (is_android) { + deps += [ + "java:system", + "java:bindings", + ] + } +} + +group("sdk") { + deps = [ + "c/system", + "cpp/bindings", + "cpp/environment:standalone", + "cpp/utility", + "interfaces/bindings", + "js", + ] +} + +static_library("libmojo_sdk") { + complete_static_lib = true + deps = [ + ":sdk", + ] +} diff --git a/mojo/public/DEPS b/mojo/public/DEPS new file mode 100644 index 0000000..23a72cd --- /dev/null +++ b/mojo/public/DEPS @@ -0,0 +1,9 @@ +include_rules = [ + # This code is checked into the chromium repo so it's fine to depend on this. + "+base", + "+build", + "+testing", + + # internal includes. + "+mojo", +] diff --git a/mojo/public/LICENSE b/mojo/public/LICENSE new file mode 100644 index 0000000..972bb2e --- /dev/null +++ b/mojo/public/LICENSE @@ -0,0 +1,27 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/mojo/public/README.md b/mojo/public/README.md new file mode 100644 index 0000000..a31a8a8 --- /dev/null +++ b/mojo/public/README.md @@ -0,0 +1,43 @@ +Mojo Public API +=============== + +The Mojo Public API is a binary stable API to the Mojo system. + +It consists of support for a number of programming languages (with a directory +for each support language), some "build" tools and build-time requirements, and +interface definitions for Mojo services (specified using an IDL). + +Note that there are various subdirectories named tests/. These contain tests of +the code in the enclosing directory, and are not meant for use by Mojo +applications. + +C/CPP/JS +-------- + +The c/, cpp/, js/ subdirectories define the API for C, C++, and JavaScript, +respectively. + +The basic principle for these directories is that they consist of the source +files that one needs at build/deployment/run time (as appropriate for the +language), organized in a natural way for the particular language. + +Interfaces +---------- + +The interfaces/ subdirectory contains Mojo IDL (a.k.a. .mojom) descriptions of +standard Mojo services. + +Platform +-------- + +The platform/ subdirectory contains any build-time requirements (e.g., static +libraries) that may be needed to produce a Mojo application for certain +platforms, such as a native shared library or as a NaCl binary. + +Tools +----- + +The tools/ subdirectory contains tools that are useful/necessary at +build/deployment time. These tools may be needed (as a practical necessity) to +use the API in any given language, e.g., to generate bindings from Mojo IDL +files. diff --git a/mojo/public/c/README.md b/mojo/public/c/README.md new file mode 100644 index 0000000..8e11545 --- /dev/null +++ b/mojo/public/c/README.md @@ -0,0 +1,45 @@ +Mojo Public C API +================= + +This directory contains C language bindings for the Mojo Public API. + +Environment +----------- + +The environment/ subdirectory defines some common things that, while not part of +the system API, may be required for GLES2 (for example). These are things that a +Mojo application may be required to provide to the GLES2 (for example) library +in order to use it. (However, the Mojo application may implement these things as +it sees fit.) + +GLES2 +----- + +The gles2/ subdirectory defines the GLES2 C API that's available to Mojo +applications. To use GLES2, Mojo applications must link against a dynamic +library (the exact mechanism being platform-dependent) and use the header files +in this directory as well as the standard Khronos GLES2 header files. + +The reason for this, rather than providing GLES2 using the standard Mojo IPC +mechanism, is performance: The protocol (and transport mechanisms) used to +communicate with the Mojo GLES2 service is not stable nor "public" (mainly for +performance reasons), and using the dynamic library shields the application from +changes to the underlying system. + +System +------ + +The system/ subdirectory provides definitions of the basic low-level API used by +all Mojo applications (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 +not, and access to the IPC mechanisms must be via the primitives defined in this +directory. + +Test Support +------------ + +This directory contains a C API for running tests. This API is only available +under special, specific test conditions. It is not meant for general use by Mojo +applications. diff --git a/mojo/public/c/environment/BUILD.gn b/mojo/public/c/environment/BUILD.gn new file mode 100644 index 0000000..3fd1689 --- /dev/null +++ b/mojo/public/c/environment/BUILD.gn @@ -0,0 +1,14 @@ +# 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. + +import("../../mojo_sdk.gni") + +mojo_sdk_source_set("environment") { + sources = [ + "async_waiter.h", + "logger.h", + ] + + mojo_sdk_deps = [ "mojo/public/c/system" ] +} diff --git a/mojo/public/c/environment/async_waiter.h b/mojo/public/c/environment/async_waiter.h new file mode 100644 index 0000000..b5a1f75 --- /dev/null +++ b/mojo/public/c/environment/async_waiter.h @@ -0,0 +1,65 @@ +// 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. + +#ifndef MOJO_PUBLIC_C_ENVIRONMENT_ASYNC_WAITER_H_ +#define MOJO_PUBLIC_C_ENVIRONMENT_ASYNC_WAITER_H_ + +#include "mojo/public/c/system/types.h" + +typedef uintptr_t MojoAsyncWaitID; + +typedef void (*MojoAsyncWaitCallback)(void* closure, MojoResult result); + +// Functions for asynchronously waiting (and cancelling asynchronous waits) on a +// handle. +// +// Thread-safety: +// - |CancelWait(wait_id)| may only be called on the same thread as the +// |AsyncWait()| that provided |wait_id| was called on. +// - A given |MojoAsyncWaiter|'s functions may only be called on the thread(s) +// that it is defined to be valid on (typically including the thread on +// which the |MojoAsyncWaiter| was provided). E.g., a library may require +// initialization with a single |MojoAsyncWaiter| and stipulate that it only +// be used on threads on which that |MojoAsyncWaiter| is valid. +// - If a |MojoAsyncWaiter| is valid on multiple threads, then its functions +// must be thread-safe (subject to the first restriction above). +struct MojoAsyncWaiter { + // Arranges for |callback| to be called on the current thread at some future + // when |handle| satisfies |signals| or it is known that it will never satisfy + // |signals| (with the same behavior as |MojoWait()|). + // + // |callback| will not be called in the nested context of |AsyncWait()|, but + // only, e.g., from some run loop. |callback| is provided with the |closure| + // argument as well as the result of the wait. For each call to |AsyncWait()|, + // |callback| will be called at most once. + // + // |handle| must not be closed or transferred (via |MojoWriteMessage()|; this + // is equivalent to closing the handle) until either the callback has been + // executed or the async wait has been cancelled using the returned (nonzero) + // |MojoAsyncWaitID| (see |CancelWait()|). Otherwise, an invalid (or, worse, + // re-used) handle may be waited on by the implementation of this + // |MojoAsyncWaiter|. + // + // Note that once the callback has been called, the returned |MojoAsyncWaitID| + // becomes invalid. + MojoAsyncWaitID (*AsyncWait)(MojoHandle handle, + MojoHandleSignals signals, + MojoDeadline deadline, + MojoAsyncWaitCallback callback, + void* closure); + + // Cancels an outstanding async wait (specified by |wait_id|) initiated by + // |AsyncWait()|. This may only be called from the same thread on which the + // corresponding |AsyncWait()| was called, and may only be called if the + // callback to |AsyncWait()| has not been called. + // + // Once this has been called, the callback provided to |AsyncWait()| will not + // be called. Moreover, it is then immediately safe to close or transfer the + // handle provided to |AsyncWait()|. (I.e., the implementation of this + // |MojoAsyncWaiter| will no longer wait on, or do anything else with, the + // handle.) + void (*CancelWait)(MojoAsyncWaitID wait_id); +}; + +#endif // MOJO_PUBLIC_C_ENVIRONMENT_ASYNC_WAITER_H_ diff --git a/mojo/public/c/environment/logger.h b/mojo/public/c/environment/logger.h new file mode 100644 index 0000000..2a9b617 --- /dev/null +++ b/mojo/public/c/environment/logger.h @@ -0,0 +1,61 @@ +// 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. + +#ifndef MOJO_PUBLIC_C_ENVIRONMENT_LOGGER_H_ +#define MOJO_PUBLIC_C_ENVIRONMENT_LOGGER_H_ + +#include <stdint.h> + +// |MojoLogLevel|: Used to specify the type of log message. Values are ordered +// by severity (i.e., higher numerical values are more severe). + +typedef int32_t MojoLogLevel; + +#ifdef __cplusplus +const MojoLogLevel MOJO_LOG_LEVEL_VERBOSE = -1; +const MojoLogLevel MOJO_LOG_LEVEL_INFO = 0; +const MojoLogLevel MOJO_LOG_LEVEL_WARNING = 1; +const MojoLogLevel MOJO_LOG_LEVEL_ERROR = 2; +const MojoLogLevel MOJO_LOG_LEVEL_FATAL = 3; +#else +#define MOJO_LOG_LEVEL_VERBOSE ((MojoLogLevel) - 1) +#define MOJO_LOG_LEVEL_INFO ((MojoLogLevel)0) +#define MOJO_LOG_LEVEL_WARNING ((MojoLogLevel)1) +#define MOJO_LOG_LEVEL_ERROR ((MojoLogLevel)2) +#define MOJO_LOG_LEVEL_FATAL ((MojoLogLevel)3) +#endif + +// Structure with basic logging functions (on top of which more friendly logging +// macros may be built). The functions are thread-safe, except for +// |SetMinimumLogLevel()| (see below). +struct MojoLogger { + // Logs |message| (which must not be null) at level |log_level| if |log_level| + // is at least the current minimum log level. If |log_level| is + // |MOJO_LOG_LEVEL_FATAL| (or greater), aborts the application/process. + // |source_file| and |source_line| indicate the source file and (1-based) line + // number, respectively; they are optional: |source_file| may be null and + // |source_line| may be zero (if |source_file| is null, then |source_line| may + // be ignored). + void (*LogMessage)(MojoLogLevel log_level, + const char* source_file, + uint32_t source_line, + const char* message); + + // Gets the minimum log level (see above), which will always be at most + // |MOJO_LOG_LEVEL_FATAL|. (Though |LogMessage()| will automatically avoid + // logging messages below the minimum log level, this may be used to avoid + // extra work.) + MojoLogLevel (*GetMinimumLogLevel)(void); + + // Sets the minimum log level (see above) to the lesser of |minimum_log_level| + // and |MOJO_LOG_LEVEL_FATAL|. + // + // Warning: This function may not be thread-safe, and should not be called + // concurrently with other |MojoLogger| functions. (In some environments -- + // such as Chromium -- that share a logger across applications, this may mean + // that it is almost never safe to call this.) + void (*SetMinimumLogLevel)(MojoLogLevel minimum_log_level); +}; + +#endif // MOJO_PUBLIC_C_ENVIRONMENT_LOGGER_H_ diff --git a/mojo/public/c/gles2/BUILD.gn b/mojo/public/c/gles2/BUILD.gn new file mode 100644 index 0000000..eb7a6f2 --- /dev/null +++ b/mojo/public/c/gles2/BUILD.gn @@ -0,0 +1,27 @@ +# 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. + +import("../../mojo_sdk.gni") + +config("gles2_config") { + defines = [ "GLES2_USE_MOJO" ] +} + +mojo_sdk_source_set("gles2") { + sources = [ + "chromium_extension.h", + "gles2.h", + "gles2_call_visitor_autogen.h", + "gles2_call_visitor_chromium_extension_autogen.h", + "gles2_export.h", + "gles2_types.h", + ] + + public_configs = [ ":gles2_config" ] + + mojo_sdk_public_deps = [ + "mojo/public/c/environment", + "mojo/public/c/system", + ] +} diff --git a/mojo/public/c/gles2/chromium_extension.h b/mojo/public/c/gles2/chromium_extension.h new file mode 100644 index 0000000..3359c7e --- /dev/null +++ b/mojo/public/c/gles2/chromium_extension.h @@ -0,0 +1,32 @@ +// Copyright 2015 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_C_GLES2_CHROMIUM_EXTENSION_H_ +#define MOJO_PUBLIC_C_GLES2_CHROMIUM_EXTENSION_H_ + +// Note: This header should be compilable as C. + +#include <GLES2/gl2.h> +#include <stdint.h> + +#include "mojo/public/c/gles2/gles2_export.h" +#include "mojo/public/c/gles2/gles2_types.h" +#include "mojo/public/c/system/types.h" + +extern "C" typedef struct _ClientBuffer* ClientBuffer; + +#ifdef __cplusplus +extern "C" { +#endif + +#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \ + MOJO_GLES2_EXPORT ReturnType GL_APIENTRY gl##Function PARAMETERS; +#include "mojo/public/c/gles2/gles2_call_visitor_chromium_extension_autogen.h" +#undef VISIT_GL_CALL + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // MOJO_PUBLIC_C_GLES2_CHROMIUM_EXTENSION_H_ diff --git a/mojo/public/c/gles2/gles2.h b/mojo/public/c/gles2/gles2.h new file mode 100644 index 0000000..b8196b9 --- /dev/null +++ b/mojo/public/c/gles2/gles2.h @@ -0,0 +1,50 @@ +// 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. + +#ifndef MOJO_PUBLIC_C_GLES2_GLES2_H_ +#define MOJO_PUBLIC_C_GLES2_GLES2_H_ + +// Note: This header should be compilable as C. + +#include <GLES2/gl2.h> +#include <stdint.h> + +#include "mojo/public/c/environment/async_waiter.h" +#include "mojo/public/c/gles2/gles2_export.h" +#include "mojo/public/c/gles2/gles2_types.h" +#include "mojo/public/c/system/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +MOJO_GLES2_EXPORT MojoGLES2Context + MojoGLES2CreateContext(MojoHandle handle, + const int32_t* attrib_list, + MojoGLES2ContextLost lost_callback, + void* closure, + const MojoAsyncWaiter* async_waiter); +MOJO_GLES2_EXPORT void MojoGLES2DestroyContext(MojoGLES2Context context); +MOJO_GLES2_EXPORT void MojoGLES2MakeCurrent(MojoGLES2Context context); +MOJO_GLES2_EXPORT void MojoGLES2SwapBuffers(void); +MOJO_GLES2_EXPORT void MojoGLES2SignalSyncPoint( + MojoGLES2Context context, + uint32_t sync_point, + MojoGLES2SignalSyncPointCallback callback, + void* closure); + +// TODO(piman): We shouldn't have to leak this interface, especially in a +// type-unsafe way. +MOJO_GLES2_EXPORT void* MojoGLES2GetGLES2Interface(MojoGLES2Context context); + +#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \ + MOJO_GLES2_EXPORT ReturnType GL_APIENTRY gl##Function PARAMETERS; +#include "mojo/public/c/gles2/gles2_call_visitor_autogen.h" +#undef VISIT_GL_CALL + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // MOJO_PUBLIC_C_GLES2_GLES2_H_ diff --git a/mojo/public/c/gles2/gles2_call_visitor_autogen.h b/mojo/public/c/gles2/gles2_call_visitor_autogen.h new file mode 100644 index 0000000..64adecf --- /dev/null +++ b/mojo/public/c/gles2/gles2_call_visitor_autogen.h @@ -0,0 +1,537 @@ +// 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. + +// This file is auto-generated from +// gpu/command_buffer/build_gles2_cmd_buffer.py +// It's formatted by clang-format using chromium coding style: +// clang-format -i -style=chromium filename +// DO NOT EDIT! + +VISIT_GL_CALL(ActiveTexture, void, (GLenum texture), (texture)) +VISIT_GL_CALL(AttachShader, + void, + (GLuint program, GLuint shader), + (program, shader)) +VISIT_GL_CALL(BindAttribLocation, + void, + (GLuint program, GLuint index, const char* name), + (program, index, name)) +VISIT_GL_CALL(BindBuffer, + void, + (GLenum target, GLuint buffer), + (target, buffer)) +VISIT_GL_CALL(BindFramebuffer, + void, + (GLenum target, GLuint framebuffer), + (target, framebuffer)) +VISIT_GL_CALL(BindRenderbuffer, + void, + (GLenum target, GLuint renderbuffer), + (target, renderbuffer)) +VISIT_GL_CALL(BindTexture, + void, + (GLenum target, GLuint texture), + (target, texture)) +VISIT_GL_CALL(BlendColor, + void, + (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha), + (red, green, blue, alpha)) +VISIT_GL_CALL(BlendEquation, void, (GLenum mode), (mode)) +VISIT_GL_CALL(BlendEquationSeparate, + void, + (GLenum modeRGB, GLenum modeAlpha), + (modeRGB, modeAlpha)) +VISIT_GL_CALL(BlendFunc, + void, + (GLenum sfactor, GLenum dfactor), + (sfactor, dfactor)) +VISIT_GL_CALL(BlendFuncSeparate, + void, + (GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha), + (srcRGB, dstRGB, srcAlpha, dstAlpha)) +VISIT_GL_CALL(BufferData, + void, + (GLenum target, GLsizeiptr size, const void* data, GLenum usage), + (target, size, data, usage)) +VISIT_GL_CALL( + BufferSubData, + void, + (GLenum target, GLintptr offset, GLsizeiptr size, const void* data), + (target, offset, size, data)) +VISIT_GL_CALL(CheckFramebufferStatus, GLenum, (GLenum target), (target)) +VISIT_GL_CALL(Clear, void, (GLbitfield mask), (mask)) +VISIT_GL_CALL(ClearColor, + void, + (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha), + (red, green, blue, alpha)) +VISIT_GL_CALL(ClearDepthf, void, (GLclampf depth), (depth)) +VISIT_GL_CALL(ClearStencil, void, (GLint s), (s)) +VISIT_GL_CALL(ColorMask, + void, + (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha), + (red, green, blue, alpha)) +VISIT_GL_CALL(CompileShader, void, (GLuint shader), (shader)) +VISIT_GL_CALL( + CompressedTexImage2D, + void, + (GLenum target, + GLint level, + GLenum internalformat, + GLsizei width, + GLsizei height, + GLint border, + GLsizei imageSize, + const void* data), + (target, level, internalformat, width, height, border, imageSize, data)) +VISIT_GL_CALL( + CompressedTexSubImage2D, + void, + (GLenum target, + GLint level, + GLint xoffset, + GLint yoffset, + GLsizei width, + GLsizei height, + GLenum format, + GLsizei imageSize, + const void* data), + (target, level, xoffset, yoffset, width, height, format, imageSize, data)) +VISIT_GL_CALL(CopyTexImage2D, + void, + (GLenum target, + GLint level, + GLenum internalformat, + GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLint border), + (target, level, internalformat, x, y, width, height, border)) +VISIT_GL_CALL(CopyTexSubImage2D, + void, + (GLenum target, + GLint level, + GLint xoffset, + GLint yoffset, + GLint x, + GLint y, + GLsizei width, + GLsizei height), + (target, level, xoffset, yoffset, x, y, width, height)) +VISIT_GL_CALL(CreateProgram, GLuint, (), ()) +VISIT_GL_CALL(CreateShader, GLuint, (GLenum type), (type)) +VISIT_GL_CALL(CullFace, void, (GLenum mode), (mode)) +VISIT_GL_CALL(DeleteBuffers, + void, + (GLsizei n, const GLuint* buffers), + (n, buffers)) +VISIT_GL_CALL(DeleteFramebuffers, + void, + (GLsizei n, const GLuint* framebuffers), + (n, framebuffers)) +VISIT_GL_CALL(DeleteProgram, void, (GLuint program), (program)) +VISIT_GL_CALL(DeleteRenderbuffers, + void, + (GLsizei n, const GLuint* renderbuffers), + (n, renderbuffers)) +VISIT_GL_CALL(DeleteShader, void, (GLuint shader), (shader)) +VISIT_GL_CALL(DeleteTextures, + void, + (GLsizei n, const GLuint* textures), + (n, textures)) +VISIT_GL_CALL(DepthFunc, void, (GLenum func), (func)) +VISIT_GL_CALL(DepthMask, void, (GLboolean flag), (flag)) +VISIT_GL_CALL(DepthRangef, void, (GLclampf zNear, GLclampf zFar), (zNear, zFar)) +VISIT_GL_CALL(DetachShader, + void, + (GLuint program, GLuint shader), + (program, shader)) +VISIT_GL_CALL(Disable, void, (GLenum cap), (cap)) +VISIT_GL_CALL(DisableVertexAttribArray, void, (GLuint index), (index)) +VISIT_GL_CALL(DrawArrays, + void, + (GLenum mode, GLint first, GLsizei count), + (mode, first, count)) +VISIT_GL_CALL(DrawElements, + void, + (GLenum mode, GLsizei count, GLenum type, const void* indices), + (mode, count, type, indices)) +VISIT_GL_CALL(Enable, void, (GLenum cap), (cap)) +VISIT_GL_CALL(EnableVertexAttribArray, void, (GLuint index), (index)) +VISIT_GL_CALL(Finish, void, (), ()) +VISIT_GL_CALL(Flush, void, (), ()) +VISIT_GL_CALL(FramebufferRenderbuffer, + void, + (GLenum target, + GLenum attachment, + GLenum renderbuffertarget, + GLuint renderbuffer), + (target, attachment, renderbuffertarget, renderbuffer)) +VISIT_GL_CALL(FramebufferTexture2D, + void, + (GLenum target, + GLenum attachment, + GLenum textarget, + GLuint texture, + GLint level), + (target, attachment, textarget, texture, level)) +VISIT_GL_CALL(FrontFace, void, (GLenum mode), (mode)) +VISIT_GL_CALL(GenBuffers, void, (GLsizei n, GLuint* buffers), (n, buffers)) +VISIT_GL_CALL(GenerateMipmap, void, (GLenum target), (target)) +VISIT_GL_CALL(GenFramebuffers, + void, + (GLsizei n, GLuint* framebuffers), + (n, framebuffers)) +VISIT_GL_CALL(GenRenderbuffers, + void, + (GLsizei n, GLuint* renderbuffers), + (n, renderbuffers)) +VISIT_GL_CALL(GenTextures, void, (GLsizei n, GLuint* textures), (n, textures)) +VISIT_GL_CALL(GetActiveAttrib, + void, + (GLuint program, + GLuint index, + GLsizei bufsize, + GLsizei* length, + GLint* size, + GLenum* type, + char* name), + (program, index, bufsize, length, size, type, name)) +VISIT_GL_CALL(GetActiveUniform, + void, + (GLuint program, + GLuint index, + GLsizei bufsize, + GLsizei* length, + GLint* size, + GLenum* type, + char* name), + (program, index, bufsize, length, size, type, name)) +VISIT_GL_CALL( + GetAttachedShaders, + void, + (GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders), + (program, maxcount, count, shaders)) +VISIT_GL_CALL(GetAttribLocation, + GLint, + (GLuint program, const char* name), + (program, name)) +VISIT_GL_CALL(GetBooleanv, + void, + (GLenum pname, GLboolean* params), + (pname, params)) +VISIT_GL_CALL(GetBufferParameteriv, + void, + (GLenum target, GLenum pname, GLint* params), + (target, pname, params)) +VISIT_GL_CALL(GetError, GLenum, (), ()) +VISIT_GL_CALL(GetFloatv, void, (GLenum pname, GLfloat* params), (pname, params)) +VISIT_GL_CALL(GetFramebufferAttachmentParameteriv, + void, + (GLenum target, GLenum attachment, GLenum pname, GLint* params), + (target, attachment, pname, params)) +VISIT_GL_CALL(GetIntegerv, void, (GLenum pname, GLint* params), (pname, params)) +VISIT_GL_CALL(GetProgramiv, + void, + (GLuint program, GLenum pname, GLint* params), + (program, pname, params)) +VISIT_GL_CALL(GetProgramInfoLog, + void, + (GLuint program, GLsizei bufsize, GLsizei* length, char* infolog), + (program, bufsize, length, infolog)) +VISIT_GL_CALL(GetRenderbufferParameteriv, + void, + (GLenum target, GLenum pname, GLint* params), + (target, pname, params)) +VISIT_GL_CALL(GetShaderiv, + void, + (GLuint shader, GLenum pname, GLint* params), + (shader, pname, params)) +VISIT_GL_CALL(GetShaderInfoLog, + void, + (GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog), + (shader, bufsize, length, infolog)) +VISIT_GL_CALL( + GetShaderPrecisionFormat, + void, + (GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision), + (shadertype, precisiontype, range, precision)) +VISIT_GL_CALL(GetShaderSource, + void, + (GLuint shader, GLsizei bufsize, GLsizei* length, char* source), + (shader, bufsize, length, source)) +VISIT_GL_CALL(GetString, const GLubyte*, (GLenum name), (name)) +VISIT_GL_CALL(GetTexParameterfv, + void, + (GLenum target, GLenum pname, GLfloat* params), + (target, pname, params)) +VISIT_GL_CALL(GetTexParameteriv, + void, + (GLenum target, GLenum pname, GLint* params), + (target, pname, params)) +VISIT_GL_CALL(GetUniformfv, + void, + (GLuint program, GLint location, GLfloat* params), + (program, location, params)) +VISIT_GL_CALL(GetUniformiv, + void, + (GLuint program, GLint location, GLint* params), + (program, location, params)) +VISIT_GL_CALL(GetUniformLocation, + GLint, + (GLuint program, const char* name), + (program, name)) +VISIT_GL_CALL(GetVertexAttribfv, + void, + (GLuint index, GLenum pname, GLfloat* params), + (index, pname, params)) +VISIT_GL_CALL(GetVertexAttribiv, + void, + (GLuint index, GLenum pname, GLint* params), + (index, pname, params)) +VISIT_GL_CALL(GetVertexAttribPointerv, + void, + (GLuint index, GLenum pname, void** pointer), + (index, pname, pointer)) +VISIT_GL_CALL(Hint, void, (GLenum target, GLenum mode), (target, mode)) +VISIT_GL_CALL(IsBuffer, GLboolean, (GLuint buffer), (buffer)) +VISIT_GL_CALL(IsEnabled, GLboolean, (GLenum cap), (cap)) +VISIT_GL_CALL(IsFramebuffer, GLboolean, (GLuint framebuffer), (framebuffer)) +VISIT_GL_CALL(IsProgram, GLboolean, (GLuint program), (program)) +VISIT_GL_CALL(IsRenderbuffer, GLboolean, (GLuint renderbuffer), (renderbuffer)) +VISIT_GL_CALL(IsShader, GLboolean, (GLuint shader), (shader)) +VISIT_GL_CALL(IsTexture, GLboolean, (GLuint texture), (texture)) +VISIT_GL_CALL(LineWidth, void, (GLfloat width), (width)) +VISIT_GL_CALL(LinkProgram, void, (GLuint program), (program)) +VISIT_GL_CALL(PixelStorei, void, (GLenum pname, GLint param), (pname, param)) +VISIT_GL_CALL(PolygonOffset, + void, + (GLfloat factor, GLfloat units), + (factor, units)) +VISIT_GL_CALL(ReadPixels, + void, + (GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLenum format, + GLenum type, + void* pixels), + (x, y, width, height, format, type, pixels)) +VISIT_GL_CALL(ReleaseShaderCompiler, void, (), ()) +VISIT_GL_CALL( + RenderbufferStorage, + void, + (GLenum target, GLenum internalformat, GLsizei width, GLsizei height), + (target, internalformat, width, height)) +VISIT_GL_CALL(SampleCoverage, + void, + (GLclampf value, GLboolean invert), + (value, invert)) +VISIT_GL_CALL(Scissor, + void, + (GLint x, GLint y, GLsizei width, GLsizei height), + (x, y, width, height)) +VISIT_GL_CALL(ShaderBinary, + void, + (GLsizei n, + const GLuint* shaders, + GLenum binaryformat, + const void* binary, + GLsizei length), + (n, shaders, binaryformat, binary, length)) +VISIT_GL_CALL(ShaderSource, + void, + (GLuint shader, + GLsizei count, + const GLchar* const* str, + const GLint* length), + (shader, count, str, length)) +VISIT_GL_CALL(StencilFunc, + void, + (GLenum func, GLint ref, GLuint mask), + (func, ref, mask)) +VISIT_GL_CALL(StencilFuncSeparate, + void, + (GLenum face, GLenum func, GLint ref, GLuint mask), + (face, func, ref, mask)) +VISIT_GL_CALL(StencilMask, void, (GLuint mask), (mask)) +VISIT_GL_CALL(StencilMaskSeparate, + void, + (GLenum face, GLuint mask), + (face, mask)) +VISIT_GL_CALL(StencilOp, + void, + (GLenum fail, GLenum zfail, GLenum zpass), + (fail, zfail, zpass)) +VISIT_GL_CALL(StencilOpSeparate, + void, + (GLenum face, GLenum fail, GLenum zfail, GLenum zpass), + (face, fail, zfail, zpass)) +VISIT_GL_CALL(TexImage2D, + void, + (GLenum target, + GLint level, + GLint internalformat, + GLsizei width, + GLsizei height, + GLint border, + GLenum format, + GLenum type, + const void* pixels), + (target, + level, + internalformat, + width, + height, + border, + format, + type, + pixels)) +VISIT_GL_CALL(TexParameterf, + void, + (GLenum target, GLenum pname, GLfloat param), + (target, pname, param)) +VISIT_GL_CALL(TexParameterfv, + void, + (GLenum target, GLenum pname, const GLfloat* params), + (target, pname, params)) +VISIT_GL_CALL(TexParameteri, + void, + (GLenum target, GLenum pname, GLint param), + (target, pname, param)) +VISIT_GL_CALL(TexParameteriv, + void, + (GLenum target, GLenum pname, const GLint* params), + (target, pname, params)) +VISIT_GL_CALL( + TexSubImage2D, + void, + (GLenum target, + GLint level, + GLint xoffset, + GLint yoffset, + GLsizei width, + GLsizei height, + GLenum format, + GLenum type, + const void* pixels), + (target, level, xoffset, yoffset, width, height, format, type, pixels)) +VISIT_GL_CALL(Uniform1f, void, (GLint location, GLfloat x), (location, x)) +VISIT_GL_CALL(Uniform1fv, + void, + (GLint location, GLsizei count, const GLfloat* v), + (location, count, v)) +VISIT_GL_CALL(Uniform1i, void, (GLint location, GLint x), (location, x)) +VISIT_GL_CALL(Uniform1iv, + void, + (GLint location, GLsizei count, const GLint* v), + (location, count, v)) +VISIT_GL_CALL(Uniform2f, + void, + (GLint location, GLfloat x, GLfloat y), + (location, x, y)) +VISIT_GL_CALL(Uniform2fv, + void, + (GLint location, GLsizei count, const GLfloat* v), + (location, count, v)) +VISIT_GL_CALL(Uniform2i, + void, + (GLint location, GLint x, GLint y), + (location, x, y)) +VISIT_GL_CALL(Uniform2iv, + void, + (GLint location, GLsizei count, const GLint* v), + (location, count, v)) +VISIT_GL_CALL(Uniform3f, + void, + (GLint location, GLfloat x, GLfloat y, GLfloat z), + (location, x, y, z)) +VISIT_GL_CALL(Uniform3fv, + void, + (GLint location, GLsizei count, const GLfloat* v), + (location, count, v)) +VISIT_GL_CALL(Uniform3i, + void, + (GLint location, GLint x, GLint y, GLint z), + (location, x, y, z)) +VISIT_GL_CALL(Uniform3iv, + void, + (GLint location, GLsizei count, const GLint* v), + (location, count, v)) +VISIT_GL_CALL(Uniform4f, + void, + (GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w), + (location, x, y, z, w)) +VISIT_GL_CALL(Uniform4fv, + void, + (GLint location, GLsizei count, const GLfloat* v), + (location, count, v)) +VISIT_GL_CALL(Uniform4i, + void, + (GLint location, GLint x, GLint y, GLint z, GLint w), + (location, x, y, z, w)) +VISIT_GL_CALL(Uniform4iv, + void, + (GLint location, GLsizei count, const GLint* v), + (location, count, v)) +VISIT_GL_CALL( + UniformMatrix2fv, + void, + (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value), + (location, count, transpose, value)) +VISIT_GL_CALL( + UniformMatrix3fv, + void, + (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value), + (location, count, transpose, value)) +VISIT_GL_CALL( + UniformMatrix4fv, + void, + (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value), + (location, count, transpose, value)) +VISIT_GL_CALL(UseProgram, void, (GLuint program), (program)) +VISIT_GL_CALL(ValidateProgram, void, (GLuint program), (program)) +VISIT_GL_CALL(VertexAttrib1f, void, (GLuint indx, GLfloat x), (indx, x)) +VISIT_GL_CALL(VertexAttrib1fv, + void, + (GLuint indx, const GLfloat* values), + (indx, values)) +VISIT_GL_CALL(VertexAttrib2f, + void, + (GLuint indx, GLfloat x, GLfloat y), + (indx, x, y)) +VISIT_GL_CALL(VertexAttrib2fv, + void, + (GLuint indx, const GLfloat* values), + (indx, values)) +VISIT_GL_CALL(VertexAttrib3f, + void, + (GLuint indx, GLfloat x, GLfloat y, GLfloat z), + (indx, x, y, z)) +VISIT_GL_CALL(VertexAttrib3fv, + void, + (GLuint indx, const GLfloat* values), + (indx, values)) +VISIT_GL_CALL(VertexAttrib4f, + void, + (GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w), + (indx, x, y, z, w)) +VISIT_GL_CALL(VertexAttrib4fv, + void, + (GLuint indx, const GLfloat* values), + (indx, values)) +VISIT_GL_CALL(VertexAttribPointer, + void, + (GLuint indx, + GLint size, + GLenum type, + GLboolean normalized, + GLsizei stride, + const void* ptr), + (indx, size, type, normalized, stride, ptr)) +VISIT_GL_CALL(Viewport, + void, + (GLint x, GLint y, GLsizei width, GLsizei height), + (x, y, width, height)) diff --git a/mojo/public/c/gles2/gles2_call_visitor_chromium_extension_autogen.h b/mojo/public/c/gles2/gles2_call_visitor_chromium_extension_autogen.h new file mode 100644 index 0000000..576a322 --- /dev/null +++ b/mojo/public/c/gles2/gles2_call_visitor_chromium_extension_autogen.h @@ -0,0 +1,569 @@ +// 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. + +// This file is auto-generated from +// gpu/command_buffer/build_gles2_cmd_buffer.py +// It's formatted by clang-format using chromium coding style: +// clang-format -i -style=chromium filename +// DO NOT EDIT! + +VISIT_GL_CALL(ShallowFinishCHROMIUM, void, (), ()) +VISIT_GL_CALL(ShallowFlushCHROMIUM, void, (), ()) +VISIT_GL_CALL(OrderingBarrierCHROMIUM, void, (), ()) +VISIT_GL_CALL( + BlitFramebufferCHROMIUM, + void, + (GLint srcX0, + GLint srcY0, + GLint srcX1, + GLint srcY1, + GLint dstX0, + GLint dstY0, + GLint dstX1, + GLint dstY1, + GLbitfield mask, + GLenum filter), + (srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter)) +VISIT_GL_CALL(RenderbufferStorageMultisampleCHROMIUM, + void, + (GLenum target, + GLsizei samples, + GLenum internalformat, + GLsizei width, + GLsizei height), + (target, samples, internalformat, width, height)) +VISIT_GL_CALL(RenderbufferStorageMultisampleEXT, + void, + (GLenum target, + GLsizei samples, + GLenum internalformat, + GLsizei width, + GLsizei height), + (target, samples, internalformat, width, height)) +VISIT_GL_CALL(FramebufferTexture2DMultisampleEXT, + void, + (GLenum target, + GLenum attachment, + GLenum textarget, + GLuint texture, + GLint level, + GLsizei samples), + (target, attachment, textarget, texture, level, samples)) +VISIT_GL_CALL(TexStorage2DEXT, + void, + (GLenum target, + GLsizei levels, + GLenum internalFormat, + GLsizei width, + GLsizei height), + (target, levels, internalFormat, width, height)) +VISIT_GL_CALL(GenQueriesEXT, void, (GLsizei n, GLuint* queries), (n, queries)) +VISIT_GL_CALL(DeleteQueriesEXT, + void, + (GLsizei n, const GLuint* queries), + (n, queries)) +VISIT_GL_CALL(QueryCounterEXT, void, (GLuint id, GLenum target), (id, target)) +VISIT_GL_CALL(IsQueryEXT, GLboolean, (GLuint id), (id)) +VISIT_GL_CALL(BeginQueryEXT, void, (GLenum target, GLuint id), (target, id)) +VISIT_GL_CALL(EndQueryEXT, void, (GLenum target), (target)) +VISIT_GL_CALL(GetQueryivEXT, + void, + (GLenum target, GLenum pname, GLint* params), + (target, pname, params)) +VISIT_GL_CALL(GetQueryObjectivEXT, + void, + (GLuint id, GLenum pname, GLint* params), + (id, pname, params)) +VISIT_GL_CALL(GetQueryObjectuivEXT, + void, + (GLuint id, GLenum pname, GLuint* params), + (id, pname, params)) +VISIT_GL_CALL(GetQueryObjecti64vEXT, + void, + (GLuint id, GLenum pname, GLint64* params), + (id, pname, params)) +VISIT_GL_CALL(GetQueryObjectui64vEXT, + void, + (GLuint id, GLenum pname, GLuint64* params), + (id, pname, params)) +VISIT_GL_CALL(SetDisjointValueSyncCHROMIUM, void, (), ()) +VISIT_GL_CALL(InsertEventMarkerEXT, + void, + (GLsizei length, const GLchar* marker), + (length, marker)) +VISIT_GL_CALL(PushGroupMarkerEXT, + void, + (GLsizei length, const GLchar* marker), + (length, marker)) +VISIT_GL_CALL(PopGroupMarkerEXT, void, (), ()) +VISIT_GL_CALL(GenVertexArraysOES, + void, + (GLsizei n, GLuint* arrays), + (n, arrays)) +VISIT_GL_CALL(DeleteVertexArraysOES, + void, + (GLsizei n, const GLuint* arrays), + (n, arrays)) +VISIT_GL_CALL(IsVertexArrayOES, GLboolean, (GLuint array), (array)) +VISIT_GL_CALL(BindVertexArrayOES, void, (GLuint array), (array)) +VISIT_GL_CALL(SwapBuffers, void, (), ()) +VISIT_GL_CALL(GetMaxValueInBufferCHROMIUM, + GLuint, + (GLuint buffer_id, GLsizei count, GLenum type, GLuint offset), + (buffer_id, count, type, offset)) +VISIT_GL_CALL(EnableFeatureCHROMIUM, + GLboolean, + (const char* feature), + (feature)) +VISIT_GL_CALL(MapBufferCHROMIUM, + void*, + (GLuint target, GLenum access), + (target, access)) +VISIT_GL_CALL(UnmapBufferCHROMIUM, GLboolean, (GLuint target), (target)) +VISIT_GL_CALL(MapBufferSubDataCHROMIUM, + void*, + (GLuint target, GLintptr offset, GLsizeiptr size, GLenum access), + (target, offset, size, access)) +VISIT_GL_CALL(UnmapBufferSubDataCHROMIUM, void, (const void* mem), (mem)) +VISIT_GL_CALL( + MapTexSubImage2DCHROMIUM, + void*, + (GLenum target, + GLint level, + GLint xoffset, + GLint yoffset, + GLsizei width, + GLsizei height, + GLenum format, + GLenum type, + GLenum access), + (target, level, xoffset, yoffset, width, height, format, type, access)) +VISIT_GL_CALL(UnmapTexSubImage2DCHROMIUM, void, (const void* mem), (mem)) +VISIT_GL_CALL(ResizeCHROMIUM, + void, + (GLuint width, GLuint height, GLfloat scale_factor), + (width, height, scale_factor)) +VISIT_GL_CALL(GetRequestableExtensionsCHROMIUM, const GLchar*, (), ()) +VISIT_GL_CALL(RequestExtensionCHROMIUM, + void, + (const char* extension), + (extension)) +VISIT_GL_CALL(GetProgramInfoCHROMIUM, + void, + (GLuint program, GLsizei bufsize, GLsizei* size, void* info), + (program, bufsize, size, info)) +VISIT_GL_CALL(CreateStreamTextureCHROMIUM, GLuint, (GLuint texture), (texture)) +VISIT_GL_CALL( + CreateImageCHROMIUM, + GLuint, + (ClientBuffer buffer, GLsizei width, GLsizei height, GLenum internalformat), + (buffer, width, height, internalformat)) +VISIT_GL_CALL(DestroyImageCHROMIUM, void, (GLuint image_id), (image_id)) +VISIT_GL_CALL( + CreateGpuMemoryBufferImageCHROMIUM, + GLuint, + (GLsizei width, GLsizei height, GLenum internalformat, GLenum usage), + (width, height, internalformat, usage)) +VISIT_GL_CALL(GetTranslatedShaderSourceANGLE, + void, + (GLuint shader, GLsizei bufsize, GLsizei* length, char* source), + (shader, bufsize, length, source)) +VISIT_GL_CALL(PostSubBufferCHROMIUM, + void, + (GLint x, GLint y, GLint width, GLint height), + (x, y, width, height)) +VISIT_GL_CALL(TexImageIOSurface2DCHROMIUM, + void, + (GLenum target, + GLsizei width, + GLsizei height, + GLuint ioSurfaceId, + GLuint plane), + (target, width, height, ioSurfaceId, plane)) +VISIT_GL_CALL(CopyTextureCHROMIUM, + void, + (GLenum target, + GLenum source_id, + GLenum dest_id, + GLint internalformat, + GLenum dest_type, + GLboolean unpack_flip_y, + GLboolean unpack_premultiply_alpha, + GLboolean unpack_unmultiply_alpha), + (target, + source_id, + dest_id, + internalformat, + dest_type, + unpack_flip_y, + unpack_premultiply_alpha, + unpack_unmultiply_alpha)) +VISIT_GL_CALL(CopySubTextureCHROMIUM, + void, + (GLenum target, + GLenum source_id, + GLenum dest_id, + GLint xoffset, + GLint yoffset, + GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLboolean unpack_flip_y, + GLboolean unpack_premultiply_alpha, + GLboolean unpack_unmultiply_alpha), + (target, + source_id, + dest_id, + xoffset, + yoffset, + x, + y, + width, + height, + unpack_flip_y, + unpack_premultiply_alpha, + unpack_unmultiply_alpha)) +VISIT_GL_CALL(CompressedCopyTextureCHROMIUM, + void, + (GLenum target, GLenum source_id, GLenum dest_id), + (target, source_id, dest_id)) +VISIT_GL_CALL( + CompressedCopySubTextureCHROMIUM, + void, + (GLenum target, + GLenum source_id, + GLenum dest_id, + GLint xoffset, + GLint yoffset, + GLint x, + GLint y, + GLsizei width, + GLsizei height), + (target, source_id, dest_id, xoffset, yoffset, x, y, width, height)) +VISIT_GL_CALL(DrawArraysInstancedANGLE, + void, + (GLenum mode, GLint first, GLsizei count, GLsizei primcount), + (mode, first, count, primcount)) +VISIT_GL_CALL(DrawElementsInstancedANGLE, + void, + (GLenum mode, + GLsizei count, + GLenum type, + const void* indices, + GLsizei primcount), + (mode, count, type, indices, primcount)) +VISIT_GL_CALL(VertexAttribDivisorANGLE, + void, + (GLuint index, GLuint divisor), + (index, divisor)) +VISIT_GL_CALL(GenMailboxCHROMIUM, void, (GLbyte * mailbox), (mailbox)) +VISIT_GL_CALL(ProduceTextureCHROMIUM, + void, + (GLenum target, const GLbyte* mailbox), + (target, mailbox)) +VISIT_GL_CALL(ProduceTextureDirectCHROMIUM, + void, + (GLuint texture, GLenum target, const GLbyte* mailbox), + (texture, target, mailbox)) +VISIT_GL_CALL(ConsumeTextureCHROMIUM, + void, + (GLenum target, const GLbyte* mailbox), + (target, mailbox)) +VISIT_GL_CALL(CreateAndConsumeTextureCHROMIUM, + GLuint, + (GLenum target, const GLbyte* mailbox), + (target, mailbox)) +VISIT_GL_CALL(BindUniformLocationCHROMIUM, + void, + (GLuint program, GLint location, const char* name), + (program, location, name)) +VISIT_GL_CALL(GenValuebuffersCHROMIUM, + void, + (GLsizei n, GLuint* buffers), + (n, buffers)) +VISIT_GL_CALL(DeleteValuebuffersCHROMIUM, + void, + (GLsizei n, const GLuint* valuebuffers), + (n, valuebuffers)) +VISIT_GL_CALL(IsValuebufferCHROMIUM, + GLboolean, + (GLuint valuebuffer), + (valuebuffer)) +VISIT_GL_CALL(BindValuebufferCHROMIUM, + void, + (GLenum target, GLuint valuebuffer), + (target, valuebuffer)) +VISIT_GL_CALL(SubscribeValueCHROMIUM, + void, + (GLenum target, GLenum subscription), + (target, subscription)) +VISIT_GL_CALL(PopulateSubscribedValuesCHROMIUM, void, (GLenum target), (target)) +VISIT_GL_CALL(UniformValuebufferCHROMIUM, + void, + (GLint location, GLenum target, GLenum subscription), + (location, target, subscription)) +VISIT_GL_CALL(BindTexImage2DCHROMIUM, + void, + (GLenum target, GLint imageId), + (target, imageId)) +VISIT_GL_CALL(ReleaseTexImage2DCHROMIUM, + void, + (GLenum target, GLint imageId), + (target, imageId)) +VISIT_GL_CALL(TraceBeginCHROMIUM, + void, + (const char* category_name, const char* trace_name), + (category_name, trace_name)) +VISIT_GL_CALL(TraceEndCHROMIUM, void, (), ()) +VISIT_GL_CALL(DiscardFramebufferEXT, + void, + (GLenum target, GLsizei count, const GLenum* attachments), + (target, count, attachments)) +VISIT_GL_CALL(LoseContextCHROMIUM, + void, + (GLenum current, GLenum other), + (current, other)) +VISIT_GL_CALL(InsertSyncPointCHROMIUM, GLuint, (), ()) +VISIT_GL_CALL(WaitSyncPointCHROMIUM, void, (GLuint sync_point), (sync_point)) +VISIT_GL_CALL(InsertFenceSyncCHROMIUM, GLuint64, (), ()) +VISIT_GL_CALL(GenSyncTokenCHROMIUM, + void, + (GLuint64 fence_sync, GLbyte* sync_token), + (fence_sync, sync_token)) +VISIT_GL_CALL(GenUnverifiedSyncTokenCHROMIUM, + void, + (GLuint64 fence_sync, GLbyte* sync_token), + (fence_sync, sync_token)) +VISIT_GL_CALL(WaitSyncTokenCHROMIUM, + void, + (const GLbyte* sync_token), + (sync_token)) +VISIT_GL_CALL(DrawBuffersEXT, + void, + (GLsizei count, const GLenum* bufs), + (count, bufs)) +VISIT_GL_CALL(DiscardBackbufferCHROMIUM, void, (), ()) +VISIT_GL_CALL(ScheduleOverlayPlaneCHROMIUM, + void, + (GLint plane_z_order, + GLenum plane_transform, + GLuint overlay_texture_id, + GLint bounds_x, + GLint bounds_y, + GLint bounds_width, + GLint bounds_height, + GLfloat uv_x, + GLfloat uv_y, + GLfloat uv_width, + GLfloat uv_height), + (plane_z_order, + plane_transform, + overlay_texture_id, + bounds_x, + bounds_y, + bounds_width, + bounds_height, + uv_x, + uv_y, + uv_width, + uv_height)) +VISIT_GL_CALL(ScheduleCALayerCHROMIUM, + void, + (GLuint contents_texture_id, + const GLfloat* contents_rect, + GLfloat opacity, + const GLuint background_color, + const GLfloat* bounds_size, + const GLfloat* transform), + (contents_texture_id, + contents_rect, + opacity, + background_color, + bounds_size, + transform)) +VISIT_GL_CALL(SwapInterval, void, (GLint interval), (interval)) +VISIT_GL_CALL(FlushDriverCachesCHROMIUM, void, (), ()) +VISIT_GL_CALL(MatrixLoadfCHROMIUM, + void, + (GLenum matrixMode, const GLfloat* m), + (matrixMode, m)) +VISIT_GL_CALL(MatrixLoadIdentityCHROMIUM, + void, + (GLenum matrixMode), + (matrixMode)) +VISIT_GL_CALL(GenPathsCHROMIUM, GLuint, (GLsizei range), (range)) +VISIT_GL_CALL(DeletePathsCHROMIUM, + void, + (GLuint path, GLsizei range), + (path, range)) +VISIT_GL_CALL(IsPathCHROMIUM, GLboolean, (GLuint path), (path)) +VISIT_GL_CALL(PathCommandsCHROMIUM, + void, + (GLuint path, + GLsizei numCommands, + const GLubyte* commands, + GLsizei numCoords, + GLenum coordType, + const GLvoid* coords), + (path, numCommands, commands, numCoords, coordType, coords)) +VISIT_GL_CALL(PathParameterfCHROMIUM, + void, + (GLuint path, GLenum pname, GLfloat value), + (path, pname, value)) +VISIT_GL_CALL(PathParameteriCHROMIUM, + void, + (GLuint path, GLenum pname, GLint value), + (path, pname, value)) +VISIT_GL_CALL(PathStencilFuncCHROMIUM, + void, + (GLenum func, GLint ref, GLuint mask), + (func, ref, mask)) +VISIT_GL_CALL(StencilFillPathCHROMIUM, + void, + (GLuint path, GLenum fillMode, GLuint mask), + (path, fillMode, mask)) +VISIT_GL_CALL(StencilStrokePathCHROMIUM, + void, + (GLuint path, GLint reference, GLuint mask), + (path, reference, mask)) +VISIT_GL_CALL(CoverFillPathCHROMIUM, + void, + (GLuint path, GLenum coverMode), + (path, coverMode)) +VISIT_GL_CALL(CoverStrokePathCHROMIUM, + void, + (GLuint path, GLenum coverMode), + (path, coverMode)) +VISIT_GL_CALL(StencilThenCoverFillPathCHROMIUM, + void, + (GLuint path, GLenum fillMode, GLuint mask, GLenum coverMode), + (path, fillMode, mask, coverMode)) +VISIT_GL_CALL(StencilThenCoverStrokePathCHROMIUM, + void, + (GLuint path, GLint reference, GLuint mask, GLenum coverMode), + (path, reference, mask, coverMode)) +VISIT_GL_CALL(StencilFillPathInstancedCHROMIUM, + void, + (GLsizei numPaths, + GLenum pathNameType, + const GLvoid* paths, + GLuint pathBase, + GLenum fillMode, + GLuint mask, + GLenum transformType, + const GLfloat* transformValues), + (numPaths, + pathNameType, + paths, + pathBase, + fillMode, + mask, + transformType, + transformValues)) +VISIT_GL_CALL(StencilStrokePathInstancedCHROMIUM, + void, + (GLsizei numPaths, + GLenum pathNameType, + const GLvoid* paths, + GLuint pathBase, + GLint reference, + GLuint mask, + GLenum transformType, + const GLfloat* transformValues), + (numPaths, + pathNameType, + paths, + pathBase, + reference, + mask, + transformType, + transformValues)) +VISIT_GL_CALL(CoverFillPathInstancedCHROMIUM, + void, + (GLsizei numPaths, + GLenum pathNameType, + const GLvoid* paths, + GLuint pathBase, + GLenum coverMode, + GLenum transformType, + const GLfloat* transformValues), + (numPaths, + pathNameType, + paths, + pathBase, + coverMode, + transformType, + transformValues)) +VISIT_GL_CALL(CoverStrokePathInstancedCHROMIUM, + void, + (GLsizei numPaths, + GLenum pathNameType, + const GLvoid* paths, + GLuint pathBase, + GLenum coverMode, + GLenum transformType, + const GLfloat* transformValues), + (numPaths, + pathNameType, + paths, + pathBase, + coverMode, + transformType, + transformValues)) +VISIT_GL_CALL(StencilThenCoverFillPathInstancedCHROMIUM, + void, + (GLsizei numPaths, + GLenum pathNameType, + const GLvoid* paths, + GLuint pathBase, + GLenum fillMode, + GLuint mask, + GLenum coverMode, + GLenum transformType, + const GLfloat* transformValues), + (numPaths, + pathNameType, + paths, + pathBase, + fillMode, + mask, + coverMode, + transformType, + transformValues)) +VISIT_GL_CALL(StencilThenCoverStrokePathInstancedCHROMIUM, + void, + (GLsizei numPaths, + GLenum pathNameType, + const GLvoid* paths, + GLuint pathBase, + GLint reference, + GLuint mask, + GLenum coverMode, + GLenum transformType, + const GLfloat* transformValues), + (numPaths, + pathNameType, + paths, + pathBase, + reference, + mask, + coverMode, + transformType, + transformValues)) +VISIT_GL_CALL(BindFragmentInputLocationCHROMIUM, + void, + (GLuint program, GLint location, const char* name), + (program, location, name)) +VISIT_GL_CALL(ProgramPathFragmentInputGenCHROMIUM, + void, + (GLuint program, + GLint location, + GLenum genMode, + GLint components, + const GLfloat* coeffs), + (program, location, genMode, components, coeffs)) +VISIT_GL_CALL(GetGraphicsResetStatusKHR, GLenum, (), ()) +VISIT_GL_CALL(BlendBarrierKHR, void, (), ()) +VISIT_GL_CALL(ApplyScreenSpaceAntialiasingCHROMIUM, void, (), ()) diff --git a/mojo/public/c/gles2/gles2_export.h b/mojo/public/c/gles2/gles2_export.h new file mode 100644 index 0000000..60667b1 --- /dev/null +++ b/mojo/public/c/gles2/gles2_export.h @@ -0,0 +1,33 @@ +// 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. + +#ifndef MOJO_PUBLIC_C_GLES2_GLES2_EXPORT_H_ +#define MOJO_PUBLIC_C_GLES2_GLES2_EXPORT_H_ + +#if defined(COMPONENT_BUILD) && defined(MOJO_USE_GLES2_IMPL) +#if defined(WIN32) + +#if defined(MOJO_GLES2_IMPLEMENTATION) +#define MOJO_GLES2_EXPORT __declspec(dllexport) +#else +#define MOJO_GLES2_EXPORT __declspec(dllimport) +#endif + +#else // !defined(WIN32) + +#if defined(MOJO_GLES2_IMPLEMENTATION) +#define MOJO_GLES2_EXPORT __attribute__((visibility("default"))) +#else +#define MOJO_GLES2_EXPORT +#endif + +#endif // defined(WIN32) + +#else // !defined(COMPONENT_BUILD) || !defined(MOJO_USE_GLES2_IMPL) + +#define MOJO_GLES2_EXPORT + +#endif // defined(COMPONENT_BUILD) && defined(MOJO_USE_GLES2_IMPL) + +#endif // MOJO_PUBLIC_C_GLES2_GLES2_EXPORT_H_ diff --git a/mojo/public/c/gles2/gles2_types.h b/mojo/public/c/gles2/gles2_types.h new file mode 100644 index 0000000..75be18b --- /dev/null +++ b/mojo/public/c/gles2/gles2_types.h @@ -0,0 +1,26 @@ +// 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. + +#ifndef MOJO_PUBLIC_C_GLES2_GLES2_TYPES_H_ +#define MOJO_PUBLIC_C_GLES2_GLES2_TYPES_H_ + +// Note: This header should be compilable as C. + +#include <stdint.h> + +#include "mojo/public/c/gles2/gles2_export.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct MojoGLES2ContextPrivate* MojoGLES2Context; +typedef void (*MojoGLES2ContextLost)(void* closure); +typedef void (*MojoGLES2SignalSyncPointCallback)(void* closure); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // MOJO_PUBLIC_C_GLES2_GLES2_TYPES_H_ diff --git a/mojo/public/c/gpu/BUILD.gn b/mojo/public/c/gpu/BUILD.gn new file mode 100644 index 0000000..c12b92a --- /dev/null +++ b/mojo/public/c/gpu/BUILD.gn @@ -0,0 +1,50 @@ +# Copyright 2015 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("../../mojo_sdk.gni") + +config("gpu_configs") { + include_dirs = [ "." ] + defines = [ "GLES2_USE_MOJO" ] +} + +group("gpu") { + public_configs = [ ":gpu_configs" ] + + deps = [ + ":MGL", + "../../platform/native:mgl_thunks", + "../../platform/native:gles2", + ] +} + +group("gpu_onscreen") { + public_deps = [ + ":MGL_onscreen", + ] + + deps = [ + ":gpu", + "../../platform/native:mgl_onscreen_thunks", + ] +} + +mojo_sdk_source_set("MGL") { + sources = [ + "MGL/mgl.h", + "MGL/mgl_types.h", + ] + + mojo_sdk_public_deps = [ "mojo/public/c/system" ] +} + +mojo_sdk_source_set("MGL_onscreen") { + sources = [ + "MGL/mgl_onscreen.h", + ] + + public_deps = [ + ":MGL", + ] +} diff --git a/mojo/public/c/gpu/DEPS b/mojo/public/c/gpu/DEPS new file mode 100644 index 0000000..9be0bc0 --- /dev/null +++ b/mojo/public/c/gpu/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+gpu", +] diff --git a/mojo/public/c/gpu/MGL/mgl.h b/mojo/public/c/gpu/MGL/mgl.h new file mode 100644 index 0000000..2633976 --- /dev/null +++ b/mojo/public/c/gpu/MGL/mgl.h @@ -0,0 +1,62 @@ +// Copyright 2015 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. + +// Note: This header should be compilable as C. + +#ifndef MOJO_PUBLIC_C_GPU_MGL_MGL_H_ +#define MOJO_PUBLIC_C_GPU_MGL_MGL_H_ + +#include <stdint.h> + +#include "mojo/public/c/gpu/MGL/mgl_types.h" +#include "mojo/public/c/system/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef uint32_t MGLOpenGLAPIVersion; + +// OpenGL ES 2.0 +#define MGL_API_VERSION_GLES2 ((MGLOpenGLAPIVersion)1) +// OpenGL ES 3.0 +#define MGL_API_VERSION_GLES3 ((MGLOpenGLAPIVersion)2) +// OpenGL ES 3.1 +#define MGL_API_VERSION_GLES31 ((MGLOpenGLAPIVersion)3) + +#define MGL_NO_CONTEXT ((MGLContext)0) + +struct MojoAsyncWaiter; + +// Creates a context at the given API version or returns MGL_NO_CONTEXT. +// |command_buffer_handle| must be a command buffer message pipe handle from +// the Gpu service or another source. The callee takes ownership of this +// handle. +// |share_group| specifies the share group to create this context in. +// If this is MGL_NO_CONTEXT a new share group will be created for this context. +// |lost_callback|, if not null, will be invoked when the context is lost. +// |async_waiter| must be a pointer to a MojoAsyncWaiter implementation that is +// usable from any thread the returned MGLContext will be used from +// for as long as the context exists. +MGLContext MGLCreateContext(MGLOpenGLAPIVersion version, + MojoHandle command_buffer_handle, + MGLContext share_group, + MGLContextLostCallback lost_callback, + void* lost_callback_closure, + const struct MojoAsyncWaiter* async_waiter); +void MGLDestroyContext(MGLContext context); + +// Makes |context| the current MGLContext for the calling thread. Calling with +// MGL_NO_CONTEXT clears the current context. +void MGLMakeCurrent(MGLContext context); + +// Returns the currently bound context for the calling thread or MGL_NO_CONTEXT +// if there is none. +MGLContext MGLGetCurrentContext(void); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // MOJO_PUBLIC_C_GPU_MGL_MGL_H_ diff --git a/mojo/public/c/gpu/MGL/mgl_onscreen.h b/mojo/public/c/gpu/MGL/mgl_onscreen.h new file mode 100644 index 0000000..f795214 --- /dev/null +++ b/mojo/public/c/gpu/MGL/mgl_onscreen.h @@ -0,0 +1,29 @@ +// Copyright 2015 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. + +// Note: This header should be compilable as C. + +#ifndef MOJO_PUBLIC_C_GPU_MGL_MGL_ONSCREEN_H_ +#define MOJO_PUBLIC_C_GPU_MGL_MGL_ONSCREEN_H_ + +#include <stdint.h> + +#include "mojo/public/c/gpu/MGL/mgl_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Resizes the default framebuffer for the currently bound onscreen MGLContext. +void MGLResizeSurface(uint32_t width, uint32_t height); + +// Presents the default framebuffer for the currently bound onscreen MGLContext +// to the windowing system or display. +void MGLSwapBuffers(); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // MOJO_PUBLIC_C_GPU_MGL_MGL_ONSCREEN_H_ diff --git a/mojo/public/c/gpu/MGL/mgl_types.h b/mojo/public/c/gpu/MGL/mgl_types.h new file mode 100644 index 0000000..2253456 --- /dev/null +++ b/mojo/public/c/gpu/MGL/mgl_types.h @@ -0,0 +1,22 @@ +// Copyright 2015 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. + +// Note: This header should be compilable as C. + +#ifndef MOJO_PUBLIC_C_GPU_MGL_TYPES_H_ +#define MOJO_PUBLIC_C_GPU_MGL_TYPES_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct MGLContextPrivate* MGLContext; +typedef void (*MGLContextLostCallback)(void* closure); +typedef void (*MGLSignalSyncPointCallback)(void* closure); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // MOJO_PUBLIC_C_GPU_MGL_TYPES_H_ diff --git a/mojo/public/c/system/BUILD.gn b/mojo/public/c/system/BUILD.gn new file mode 100644 index 0000000..51fc58d --- /dev/null +++ b/mojo/public/c/system/BUILD.gn @@ -0,0 +1,60 @@ +# 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. + +import("../../mojo_sdk.gni") + +# Depend on this target to use the types etc defined in the system without +# linking against a specific implementation of the system. To link against a +# particular implementation, use the :for_component or +# :for_shared_library targets, depending on the type of target you are. +mojo_sdk_source_set("system") { + sources = [ + "buffer.h", + "core.h", + "data_pipe.h", + "functions.h", + "macros.h", + "message_pipe.h", + "system_export.h", + "types.h", + ] +} + +# In an is_component_build build, everything can link against //mojo/edk/system +# because it is built as a shared library. However, in a static build, +# //mojo/edk/system is linked into an executable (e.g., mojo_shell), and must be +# injected into other shared libraries (i.e., Mojo Apps) that need the mojo +# system API. +# +# For component targets, add //mojo/public/c/system:for_component to your deps +# section. +# +# For shared_library targets (e.g., a Mojo App), add +# //mojo/public/c/system:for_shared_library to your deps + +group("for_shared_library") { + public_deps = [ + ":system", + ] + if (is_component_build) { + deps = [ + "//third_party/mojo/src/mojo/edk/system", + ] + } else { + deps = [ + "../../platform/native:system", + ] + } +} + +group("for_component") { + public_deps = [ + ":system", + ] + if (is_component_build) { + deps = [ + "//third_party/mojo/src/mojo/edk/system", + ] + } +} diff --git a/mojo/public/c/system/buffer.h b/mojo/public/c/system/buffer.h new file mode 100644 index 0000000..45d2c2d --- /dev/null +++ b/mojo/public/c/system/buffer.h @@ -0,0 +1,186 @@ +// 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. + +// This file contains types/constants and functions specific to buffers (and in +// particular shared buffers). +// TODO(vtl): Reorganize this file (etc.) to separate general buffer functions +// from (shared) buffer creation. +// +// Note: This header should be compilable as C. + +#ifndef MOJO_PUBLIC_C_SYSTEM_BUFFER_H_ +#define MOJO_PUBLIC_C_SYSTEM_BUFFER_H_ + +#include "mojo/public/c/system/macros.h" +#include "mojo/public/c/system/system_export.h" +#include "mojo/public/c/system/types.h" + +// |MojoCreateSharedBufferOptions|: Used to specify creation parameters for a +// shared buffer to |MojoCreateSharedBuffer()|. +// |uint32_t struct_size|: Set to the size of the +// |MojoCreateSharedBufferOptions| struct. (Used to allow for future +// extensions.) +// |MojoCreateSharedBufferOptionsFlags flags|: Reserved for future use. +// |MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE|: No flags; default mode. +// +// TODO(vtl): Maybe add a flag to indicate whether the memory should be +// executable or not? +// TODO(vtl): Also a flag for discardable (ashmem-style) buffers. + +typedef uint32_t MojoCreateSharedBufferOptionsFlags; + +#ifdef __cplusplus +const MojoCreateSharedBufferOptionsFlags + MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE = 0; +#else +#define MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE \ + ((MojoCreateSharedBufferOptionsFlags)0) +#endif + +MOJO_STATIC_ASSERT(MOJO_ALIGNOF(int64_t) == 8, "int64_t has weird alignment"); +struct MOJO_ALIGNAS(8) MojoCreateSharedBufferOptions { + uint32_t struct_size; + MojoCreateSharedBufferOptionsFlags flags; +}; +MOJO_STATIC_ASSERT(sizeof(MojoCreateSharedBufferOptions) == 8, + "MojoCreateSharedBufferOptions has wrong size"); + +// |MojoDuplicateBufferHandleOptions|: Used to specify parameters in duplicating +// access to a shared buffer to |MojoDuplicateBufferHandle()|. +// |uint32_t struct_size|: Set to the size of the +// |MojoDuplicateBufferHandleOptions| struct. (Used to allow for future +// extensions.) +// |MojoDuplicateBufferHandleOptionsFlags flags|: Reserved for future use. +// |MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE|: No flags; default +// mode. +// +// TODO(vtl): Add flags to remove writability (and executability)? Also, COW? + +typedef uint32_t MojoDuplicateBufferHandleOptionsFlags; + +#ifdef __cplusplus +const MojoDuplicateBufferHandleOptionsFlags + MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE = 0; +#else +#define MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE \ + ((MojoDuplicateBufferHandleOptionsFlags)0) +#endif + +struct MojoDuplicateBufferHandleOptions { + uint32_t struct_size; + MojoDuplicateBufferHandleOptionsFlags flags; +}; +MOJO_STATIC_ASSERT(sizeof(MojoDuplicateBufferHandleOptions) == 8, + "MojoDuplicateBufferHandleOptions has wrong size"); + +// |MojoMapBufferFlags|: Used to specify different modes to |MojoMapBuffer()|. +// |MOJO_MAP_BUFFER_FLAG_NONE| - No flags; default mode. + +typedef uint32_t MojoMapBufferFlags; + +#ifdef __cplusplus +const MojoMapBufferFlags MOJO_MAP_BUFFER_FLAG_NONE = 0; +#else +#define MOJO_MAP_BUFFER_FLAG_NONE ((MojoMapBufferFlags)0) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// Note: See the comment in functions.h about the meaning of the "optional" +// label for pointer parameters. + +// Creates a buffer of size |num_bytes| bytes that can be shared between +// applications (by duplicating the handle -- see |MojoDuplicateBufferHandle()| +// -- and passing it over a message pipe). To access the buffer, one must call +// |MojoMapBuffer()|. +// +// |options| may be set to null for a shared buffer with the default options. +// +// On success, |*shared_buffer_handle| will be set to the handle for the shared +// buffer. (On failure, it is not modified.) +// +// Note: While more than |num_bytes| bytes may apparently be +// available/visible/readable/writable, trying to use those extra bytes is +// undefined behavior. +// +// Returns: +// |MOJO_RESULT_OK| on success. +// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g., +// |*options| is invalid). +// |MOJO_RESULT_RESOURCE_EXHAUSTED| if a process/system/quota/etc. limit has +// been reached (e.g., if the requested size was too large, or if the +// maximum number of handles was exceeded). +// |MOJO_RESULT_UNIMPLEMENTED| if an unsupported flag was set in |*options|. +MOJO_SYSTEM_EXPORT MojoResult MojoCreateSharedBuffer( + const struct MojoCreateSharedBufferOptions* options, // Optional. + uint64_t num_bytes, // In. + MojoHandle* shared_buffer_handle); // Out. + +// Duplicates the handle |buffer_handle| to a buffer. This creates another +// handle (returned in |*new_buffer_handle| on success), which can then be sent +// to another application over a message pipe, while retaining access to the +// |buffer_handle| (and any mappings that it may have). +// +// |options| may be set to null to duplicate the buffer handle with the default +// options. +// +// On success, |*shared_buffer_handle| will be set to the handle for the new +// buffer handle. (On failure, it is not modified.) +// +// Returns: +// |MOJO_RESULT_OK| on success. +// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g., +// |buffer_handle| is not a valid buffer handle or |*options| is invalid). +// |MOJO_RESULT_UNIMPLEMENTED| if an unsupported flag was set in |*options|. +MOJO_SYSTEM_EXPORT MojoResult MojoDuplicateBufferHandle( + MojoHandle buffer_handle, + const struct MojoDuplicateBufferHandleOptions* options, // Optional. + MojoHandle* new_buffer_handle); // Out. + +// Maps the part (at offset |offset| of length |num_bytes|) of the buffer given +// by |buffer_handle| into memory, with options specified by |flags|. |offset + +// num_bytes| must be less than or equal to the size of the buffer. On success, +// |*buffer| points to memory with the requested part of the buffer. (On +// failure, it is not modified.) +// +// A single buffer handle may have multiple active mappings (possibly depending +// on the buffer type). The permissions (e.g., writable or executable) of the +// returned memory may depend on the properties of the buffer and properties +// attached to the buffer handle as well as |flags|. +// +// Note: Though data outside the specified range may apparently be +// available/visible/readable/writable, trying to use those extra bytes is +// undefined behavior. +// +// Returns: +// |MOJO_RESULT_OK| on success. +// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g., +// |buffer_handle| is not a valid buffer handle or the range specified by +// |offset| and |num_bytes| is not valid). +// |MOJO_RESULT_RESOURCE_EXHAUSTED| if the mapping operation itself failed +// (e.g., due to not having appropriate address space available). +MOJO_SYSTEM_EXPORT MojoResult MojoMapBuffer(MojoHandle buffer_handle, + uint64_t offset, + uint64_t num_bytes, + void** buffer, // Out. + MojoMapBufferFlags flags); + +// Unmaps a buffer pointer that was mapped by |MojoMapBuffer()|. |buffer| must +// have been the result of |MojoMapBuffer()| (not some other pointer inside +// the mapped memory), and the entire mapping will be removed (partial unmapping +// is not supported). A mapping may only be unmapped once. +// +// Returns: +// |MOJO_RESULT_OK| on success. +// |MOJO_RESULT_INVALID_ARGUMENT| if |buffer| is invalid (e.g., is not the +// result of |MojoMapBuffer()| or has already been unmapped). +MOJO_SYSTEM_EXPORT MojoResult MojoUnmapBuffer(void* buffer); // In. + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // MOJO_PUBLIC_C_SYSTEM_BUFFER_H_ diff --git a/mojo/public/c/system/core.h b/mojo/public/c/system/core.h new file mode 100644 index 0000000..0e78786 --- /dev/null +++ b/mojo/public/c/system/core.h @@ -0,0 +1,21 @@ +// 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. + +// This is a catch-all header that includes everything. +// +// Note: This header should be compilable as C. + +#ifndef MOJO_PUBLIC_C_SYSTEM_CORE_H_ +#define MOJO_PUBLIC_C_SYSTEM_CORE_H_ + +#include "mojo/public/c/system/buffer.h" +#include "mojo/public/c/system/data_pipe.h" +#include "mojo/public/c/system/functions.h" +#include "mojo/public/c/system/macros.h" +#include "mojo/public/c/system/main.h" +#include "mojo/public/c/system/message_pipe.h" +#include "mojo/public/c/system/system_export.h" +#include "mojo/public/c/system/types.h" + +#endif // MOJO_PUBLIC_C_SYSTEM_CORE_H_ diff --git a/mojo/public/c/system/data_pipe.h b/mojo/public/c/system/data_pipe.h new file mode 100644 index 0000000..54cfea8 --- /dev/null +++ b/mojo/public/c/system/data_pipe.h @@ -0,0 +1,366 @@ +// 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. + +// This file contains types/constants and functions specific to data pipes. +// +// Note: This header should be compilable as C. + +#ifndef MOJO_PUBLIC_C_SYSTEM_DATA_PIPE_H_ +#define MOJO_PUBLIC_C_SYSTEM_DATA_PIPE_H_ + +#include "mojo/public/c/system/macros.h" +#include "mojo/public/c/system/system_export.h" +#include "mojo/public/c/system/types.h" + +// |MojoCreateDataPipeOptions|: Used to specify creation parameters for a data +// pipe to |MojoCreateDataPipe()|. +// |uint32_t struct_size|: Set to the size of the |MojoCreateDataPipeOptions| +// struct. (Used to allow for future extensions.) +// |MojoCreateDataPipeOptionsFlags flags|: Used to specify different modes of +// operation. +// |MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE|: No flags; default mode. +// |uint32_t element_num_bytes|: The size of an element, in bytes. All +// transactions and buffers will consist of an integral number of +// elements. Must be nonzero. +// |uint32_t capacity_num_bytes|: The capacity of the data pipe, in number of +// bytes; must be a multiple of |element_num_bytes|. The data pipe will +// always be able to queue AT LEAST this much data. Set to zero to opt for +// a system-dependent automatically-calculated capacity (which will always +// be at least one element). + +typedef uint32_t MojoCreateDataPipeOptionsFlags; + +#ifdef __cplusplus +const MojoCreateDataPipeOptionsFlags MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE = + 0; +#else +#define MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE \ + ((MojoCreateDataPipeOptionsFlags)0) +#endif + +MOJO_STATIC_ASSERT(MOJO_ALIGNOF(int64_t) == 8, "int64_t has weird alignment"); +struct MOJO_ALIGNAS(8) MojoCreateDataPipeOptions { + uint32_t struct_size; + MojoCreateDataPipeOptionsFlags flags; + uint32_t element_num_bytes; + uint32_t capacity_num_bytes; +}; +MOJO_STATIC_ASSERT(sizeof(MojoCreateDataPipeOptions) == 16, + "MojoCreateDataPipeOptions has wrong size"); + +// |MojoWriteDataFlags|: Used to specify different modes to |MojoWriteData()| +// and |MojoBeginWriteData()|. +// |MOJO_WRITE_DATA_FLAG_NONE| - No flags; default mode. +// |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| - Write either all the elements +// requested or none of them. + +typedef uint32_t MojoWriteDataFlags; + +#ifdef __cplusplus +const MojoWriteDataFlags MOJO_WRITE_DATA_FLAG_NONE = 0; +const MojoWriteDataFlags MOJO_WRITE_DATA_FLAG_ALL_OR_NONE = 1 << 0; +#else +#define MOJO_WRITE_DATA_FLAG_NONE ((MojoWriteDataFlags)0) +#define MOJO_WRITE_DATA_FLAG_ALL_OR_NONE ((MojoWriteDataFlags)1 << 0) +#endif + +// |MojoReadDataFlags|: Used to specify different modes to |MojoReadData()| and +// |MojoBeginReadData()|. +// |MOJO_READ_DATA_FLAG_NONE| - No flags; default mode. +// |MOJO_READ_DATA_FLAG_ALL_OR_NONE| - Read (or discard) either the requested +// number of elements or none. +// |MOJO_READ_DATA_FLAG_DISCARD| - Discard (up to) the requested number of +// elements. +// |MOJO_READ_DATA_FLAG_QUERY| - Query the number of elements available to +// read. For use with |MojoReadData()| only. Mutually exclusive with +// |MOJO_READ_DATA_FLAG_DISCARD|, and |MOJO_READ_DATA_FLAG_ALL_OR_NONE| +// is ignored if this flag is set. +// |MOJO_READ_DATA_FLAG_PEEK| - Read elements without removing them. For use +// with |MojoReadData()| only. Mutually exclusive with +// |MOJO_READ_DATA_FLAG_DISCARD| and |MOJO_READ_DATA_FLAG_QUERY|. + +typedef uint32_t MojoReadDataFlags; + +#ifdef __cplusplus +const MojoReadDataFlags MOJO_READ_DATA_FLAG_NONE = 0; +const MojoReadDataFlags MOJO_READ_DATA_FLAG_ALL_OR_NONE = 1 << 0; +const MojoReadDataFlags MOJO_READ_DATA_FLAG_DISCARD = 1 << 1; +const MojoReadDataFlags MOJO_READ_DATA_FLAG_QUERY = 1 << 2; +const MojoReadDataFlags MOJO_READ_DATA_FLAG_PEEK = 1 << 3; +#else +#define MOJO_READ_DATA_FLAG_NONE ((MojoReadDataFlags)0) +#define MOJO_READ_DATA_FLAG_ALL_OR_NONE ((MojoReadDataFlags)1 << 0) +#define MOJO_READ_DATA_FLAG_DISCARD ((MojoReadDataFlags)1 << 1) +#define MOJO_READ_DATA_FLAG_QUERY ((MojoReadDataFlags)1 << 2) +#define MOJO_READ_DATA_FLAG_PEEK ((MojoReadDataFlags)1 << 3) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// Note: See the comment in functions.h about the meaning of the "optional" +// label for pointer parameters. + +// Creates a data pipe, which is a unidirectional communication channel for +// unframed data, with the given options. Data is unframed, but must come as +// (multiples of) discrete elements, of the size given in |options|. See +// |MojoCreateDataPipeOptions| for a description of the different options +// available for data pipes. +// +// |options| may be set to null for a data pipe with the default options (which +// will have an element size of one byte and have some system-dependent +// capacity). +// +// On success, |*data_pipe_producer_handle| will be set to the handle for the +// producer and |*data_pipe_consumer_handle| will be set to the handle for the +// consumer. (On failure, they are not modified.) +// +// Returns: +// |MOJO_RESULT_OK| on success. +// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g., +// |*options| is invalid). +// |MOJO_RESULT_RESOURCE_EXHAUSTED| if a process/system/quota/etc. limit has +// been reached (e.g., if the requested capacity was too large, or if the +// maximum number of handles was exceeded). +// |MOJO_RESULT_UNIMPLEMENTED| if an unsupported flag was set in |*options|. +MOJO_SYSTEM_EXPORT MojoResult MojoCreateDataPipe( + const struct MojoCreateDataPipeOptions* options, // Optional. + MojoHandle* data_pipe_producer_handle, // Out. + MojoHandle* data_pipe_consumer_handle); // Out. + +// Writes the given data to the data pipe producer given by +// |data_pipe_producer_handle|. |elements| points to data of size |*num_bytes|; +// |*num_bytes| should be a multiple of the data pipe's element size. If +// |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| is set in |flags|, either all the data +// will be written or none is. +// +// On success, |*num_bytes| is set to the amount of data that was actually +// written. +// +// Note: If the data pipe has the "may discard" option flag (specified on +// creation), this will discard as much data as required to write the given +// data, starting with the earliest written data that has not been consumed. +// However, even with "may discard", if |*num_bytes| is greater than the data +// pipe's capacity (and |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| is not set), this +// will write the maximum amount possible (namely, the data pipe's capacity) and +// set |*num_bytes| to that amount. It will *not* discard data from |elements|. +// +// Returns: +// |MOJO_RESULT_OK| on success. +// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g., +// |data_pipe_producer_dispatcher| is not a handle to a data pipe +// producer or |*num_bytes| is not a multiple of the data pipe's element +// size). +// |MOJO_RESULT_FAILED_PRECONDITION| if the data pipe consumer handle has been +// closed. +// |MOJO_RESULT_OUT_OF_RANGE| if |flags| has +// |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| set and the required amount of data +// (specified by |*num_bytes|) could not be written. +// |MOJO_RESULT_BUSY| if there is a two-phase write ongoing with +// |data_pipe_producer_handle| (i.e., |MojoBeginWriteData()| has been +// called, but not yet the matching |MojoEndWriteData()|). +// |MOJO_RESULT_SHOULD_WAIT| if no data can currently be written (and the +// consumer is still open) and |flags| does *not* have +// |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| set. +// +// TODO(vtl): Should there be a way of querying how much data can be written? +MOJO_SYSTEM_EXPORT MojoResult + MojoWriteData(MojoHandle data_pipe_producer_handle, + const void* elements, + uint32_t* num_bytes, // In/out. + MojoWriteDataFlags flags); + +// Begins a two-phase write to the data pipe producer given by +// |data_pipe_producer_handle|. On success, |*buffer| will be a pointer to which +// the caller can write |*buffer_num_bytes| bytes of data. If flags has +// |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| set, then the output value +// |*buffer_num_bytes| will be at least as large as its input value, which must +// also be a multiple of the element size (if |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| +// is not set, the input value of |*buffer_num_bytes| is ignored). +// +// During a two-phase write, |data_pipe_producer_handle| is *not* writable. +// E.g., if another thread tries to write to it, it will get |MOJO_RESULT_BUSY|; +// that thread can then wait for |data_pipe_producer_handle| to become writable +// again. +// +// When |MojoBeginWriteData()| returns MOJO_RESULT_OK, and the caller has +// finished writing data to |*buffer|, it should call |MojoEndWriteData()| to +// specify the amount written and to complete the two-phase write. +// |MojoEndWriteData()| need not be called for other return values. +// +// Note: If the data pipe has the "may discard" option flag (specified on +// creation) and |flags| has |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| set, this may +// discard some data. +// +// Returns: +// |MOJO_RESULT_OK| on success. +// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g., +// |data_pipe_producer_handle| is not a handle to a data pipe producer or +// flags has |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| set and +// |*buffer_num_bytes| is not a multiple of the element size). +// |MOJO_RESULT_FAILED_PRECONDITION| if the data pipe consumer handle has been +// closed. +// |MOJO_RESULT_OUT_OF_RANGE| if |flags| has +// |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| set and the required amount of data +// (specified by |*buffer_num_bytes|) cannot be written contiguously at +// this time. (Note that there may be space available for the required +// amount of data, but the "next" write position may not be large enough.) +// |MOJO_RESULT_BUSY| if there is already a two-phase write ongoing with +// |data_pipe_producer_handle| (i.e., |MojoBeginWriteData()| has been +// called, but not yet the matching |MojoEndWriteData()|). +// |MOJO_RESULT_SHOULD_WAIT| if no data can currently be written (and the +// consumer is still open). +MOJO_SYSTEM_EXPORT MojoResult + MojoBeginWriteData(MojoHandle data_pipe_producer_handle, + void** buffer, // Out. + uint32_t* buffer_num_bytes, // In/out. + MojoWriteDataFlags flags); + +// Ends a two-phase write to the data pipe producer given by +// |data_pipe_producer_handle| that was begun by a call to +// |MojoBeginWriteData()| on the same handle. |num_bytes_written| should +// indicate the amount of data actually written; it must be less than or equal +// to the value of |*buffer_num_bytes| output by |MojoBeginWriteData()| and must +// be a multiple of the element size. The buffer given by |*buffer| from +// |MojoBeginWriteData()| must have been filled with exactly |num_bytes_written| +// bytes of data. +// +// On failure, the two-phase write (if any) is ended (so the handle may become +// writable again, if there's space available) but no data written to |*buffer| +// is "put into" the data pipe. +// +// Returns: +// |MOJO_RESULT_OK| on success. +// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g., +// |data_pipe_producer_handle| is not a handle to a data pipe producer or +// |num_bytes_written| is invalid (greater than the maximum value provided +// by |MojoBeginWriteData()| or not a multiple of the element size). +// |MOJO_RESULT_FAILED_PRECONDITION| if the data pipe producer is not in a +// two-phase write (e.g., |MojoBeginWriteData()| was not called or +// |MojoEndWriteData()| has already been called). +MOJO_SYSTEM_EXPORT MojoResult + MojoEndWriteData(MojoHandle data_pipe_producer_handle, + uint32_t num_bytes_written); + +// Reads data from the data pipe consumer given by |data_pipe_consumer_handle|. +// May also be used to discard data or query the amount of data available. +// +// If |flags| has neither |MOJO_READ_DATA_FLAG_DISCARD| nor +// |MOJO_READ_DATA_FLAG_QUERY| set, this tries to read up to |*num_bytes| (which +// must be a multiple of the data pipe's element size) bytes of data to +// |elements| and set |*num_bytes| to the amount actually read. If flags has +// |MOJO_READ_DATA_FLAG_ALL_OR_NONE| set, it will either read exactly +// |*num_bytes| bytes of data or none. Additionally, if flags has +// |MOJO_READ_DATA_FLAG_PEEK| set, the data read will remain in the pipe and be +// available to future reads. +// +// If flags has |MOJO_READ_DATA_FLAG_DISCARD| set, it discards up to +// |*num_bytes| (which again must be a multiple of the element size) bytes of +// data, setting |*num_bytes| to the amount actually discarded. If flags has +// |MOJO_READ_DATA_FLAG_ALL_OR_NONE|, it will either discard exactly +// |*num_bytes| bytes of data or none. In this case, |MOJO_READ_DATA_FLAG_QUERY| +// must not be set, and |elements| is ignored (and should typically be set to +// null). +// +// If flags has |MOJO_READ_DATA_FLAG_QUERY| set, it queries the amount of data +// available, setting |*num_bytes| to the number of bytes available. In this +// case, |MOJO_READ_DATA_FLAG_DISCARD| must not be set, and +// |MOJO_READ_DATA_FLAG_ALL_OR_NONE| is ignored, as are |elements| and the input +// value of |*num_bytes|. +// +// Returns: +// |MOJO_RESULT_OK| on success (see above for a description of the different +// operations). +// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g., +// |data_pipe_consumer_handle| is invalid, the combination of flags in +// |flags| is invalid, etc.). +// |MOJO_RESULT_FAILED_PRECONDITION| if the data pipe producer handle has been +// closed and data (or the required amount of data) was not available to +// be read or discarded. +// |MOJO_RESULT_OUT_OF_RANGE| if |flags| has |MOJO_READ_DATA_FLAG_ALL_OR_NONE| +// set and the required amount of data is not available to be read or +// discarded (and the producer is still open). +// |MOJO_RESULT_BUSY| if there is a two-phase read ongoing with +// |data_pipe_consumer_handle| (i.e., |MojoBeginReadData()| has been +// called, but not yet the matching |MojoEndReadData()|). +// |MOJO_RESULT_SHOULD_WAIT| if there is no data to be read or discarded (and +// the producer is still open) and |flags| does *not* have +// |MOJO_READ_DATA_FLAG_ALL_OR_NONE| set. +MOJO_SYSTEM_EXPORT MojoResult MojoReadData(MojoHandle data_pipe_consumer_handle, + void* elements, // Out. + uint32_t* num_bytes, // In/out. + MojoReadDataFlags flags); + +// Begins a two-phase read from the data pipe consumer given by +// |data_pipe_consumer_handle|. On success, |*buffer| will be a pointer from +// which the caller can read |*buffer_num_bytes| bytes of data. If flags has +// |MOJO_READ_DATA_FLAG_ALL_OR_NONE| set, then the output value +// |*buffer_num_bytes| will be at least as large as its input value, which must +// also be a multiple of the element size (if |MOJO_READ_DATA_FLAG_ALL_OR_NONE| +// is not set, the input value of |*buffer_num_bytes| is ignored). |flags| must +// not have |MOJO_READ_DATA_FLAG_DISCARD|, |MOJO_READ_DATA_FLAG_QUERY|, or +// |MOJO_READ_DATA_FLAG_PEEK| set. +// +// During a two-phase read, |data_pipe_consumer_handle| is *not* readable. +// E.g., if another thread tries to read from it, it will get +// |MOJO_RESULT_BUSY|; that thread can then wait for |data_pipe_consumer_handle| +// to become readable again. +// +// Once the caller has finished reading data from |*buffer|, it should call +// |MojoEndReadData()| to specify the amount read and to complete the two-phase +// read. +// +// Returns: +// |MOJO_RESULT_OK| on success. +// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g., +// |data_pipe_consumer_handle| is not a handle to a data pipe consumer, +// |flags| has |MOJO_READ_DATA_FLAG_ALL_OR_NONE| set and +// |*buffer_num_bytes| is not a multiple of the element size, or |flags| +// has invalid flags set). +// |MOJO_RESULT_FAILED_PRECONDITION| if the data pipe producer handle has been +// closed. +// |MOJO_RESULT_OUT_OF_RANGE| if |flags| has |MOJO_READ_DATA_FLAG_ALL_OR_NONE| +// set and the required amount of data (specified by |*buffer_num_bytes|) +// cannot be read from a contiguous buffer at this time. (Note that there +// may be the required amount of data, but it may not be contiguous.) +// |MOJO_RESULT_BUSY| if there is already a two-phase read ongoing with +// |data_pipe_consumer_handle| (i.e., |MojoBeginReadData()| has been +// called, but not yet the matching |MojoEndReadData()|). +// |MOJO_RESULT_SHOULD_WAIT| if no data can currently be read (and the +// producer is still open). +MOJO_SYSTEM_EXPORT MojoResult + MojoBeginReadData(MojoHandle data_pipe_consumer_handle, + const void** buffer, // Out. + uint32_t* buffer_num_bytes, // In/out. + MojoReadDataFlags flags); + +// Ends a two-phase read from the data pipe consumer given by +// |data_pipe_consumer_handle| that was begun by a call to |MojoBeginReadData()| +// on the same handle. |num_bytes_read| should indicate the amount of data +// actually read; it must be less than or equal to the value of +// |*buffer_num_bytes| output by |MojoBeginReadData()| and must be a multiple of +// the element size. +// +// On failure, the two-phase read (if any) is ended (so the handle may become +// readable again) but no data is "removed" from the data pipe. +// +// Returns: +// |MOJO_RESULT_OK| on success. +// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g., +// |data_pipe_consumer_handle| is not a handle to a data pipe consumer or +// |num_bytes_written| is greater than the maximum value provided by +// |MojoBeginReadData()| or not a multiple of the element size). +// |MOJO_RESULT_FAILED_PRECONDITION| if the data pipe consumer is not in a +// two-phase read (e.g., |MojoBeginReadData()| was not called or +// |MojoEndReadData()| has already been called). +MOJO_SYSTEM_EXPORT MojoResult + MojoEndReadData(MojoHandle data_pipe_consumer_handle, + uint32_t num_bytes_read); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // MOJO_PUBLIC_C_SYSTEM_DATA_PIPE_H_ diff --git a/mojo/public/c/system/functions.h b/mojo/public/c/system/functions.h new file mode 100644 index 0000000..8b576c1 --- /dev/null +++ b/mojo/public/c/system/functions.h @@ -0,0 +1,134 @@ +// 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. + +// This file contains basic functions common to different Mojo system APIs. +// +// Note: This header should be compilable as C. + +#ifndef MOJO_PUBLIC_C_SYSTEM_FUNCTIONS_H_ +#define MOJO_PUBLIC_C_SYSTEM_FUNCTIONS_H_ + +#include "mojo/public/c/system/system_export.h" +#include "mojo/public/c/system/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Note: Pointer parameters that are labelled "optional" may be null (at least +// under some circumstances). Non-const pointer parameters are also labeled +// "in", "out", or "in/out", to indicate how they are used. (Note that how/if +// such a parameter is used may depend on other parameters or the requested +// operation's success/failure. E.g., a separate |flags| parameter may control +// whether a given "in/out" parameter is used for input, output, or both.) + +// Returns the time, in microseconds, since some undefined point in the past. +// The values are only meaningful relative to other values that were obtained +// from the same device without an intervening system restart. Such values are +// guaranteed to be monotonically non-decreasing with the passage of real time. +// Although the units are microseconds, the resolution of the clock may vary and +// is typically in the range of ~1-15 ms. +MOJO_SYSTEM_EXPORT MojoTimeTicks MojoGetTimeTicksNow(void); + +// Closes the given |handle|. +// +// Returns: +// |MOJO_RESULT_OK| on success. +// |MOJO_RESULT_INVALID_ARGUMENT| if |handle| is not a valid handle. +// +// Concurrent operations on |handle| may succeed (or fail as usual) if they +// happen before the close, be cancelled with result |MOJO_RESULT_CANCELLED| if +// they properly overlap (this is likely the case with |MojoWait()|, etc.), or +// fail with |MOJO_RESULT_INVALID_ARGUMENT| if they happen after. +MOJO_SYSTEM_EXPORT MojoResult MojoClose(MojoHandle handle); + +// Waits on the given handle until one of the following happens: +// - A signal indicated by |signals| is satisfied. +// - It becomes known that no signal indicated by |signals| will ever be +// satisfied. (See the description of the |MOJO_RESULT_CANCELLED| and +// |MOJO_RESULT_FAILED_PRECONDITION| return values below.) +// - Until |deadline| has passed. +// +// If |deadline| is |MOJO_DEADLINE_INDEFINITE|, this will wait "forever" (until +// one of the other wait termination conditions is satisfied). If |deadline| is +// 0, this will return |MOJO_RESULT_DEADLINE_EXCEEDED| only if one of the other +// termination conditions (e.g., a signal is satisfied, or all signals are +// unsatisfiable) is not already satisfied. +// +// |signals_state| (optional): See documentation for |MojoHandleSignalsState|. +// +// Returns: +// |MOJO_RESULT_OK| if some signal in |signals| was satisfied (or is already +// satisfied). +// |MOJO_RESULT_CANCELLED| if |handle| was closed (necessarily from another +// thread) during the wait. +// |MOJO_RESULT_INVALID_ARGUMENT| if |handle| is not a valid handle (e.g., if +// it has already been closed). The |signals_state| value is unchanged. +// |MOJO_RESULT_DEADLINE_EXCEEDED| if the deadline has passed without any of +// the signals being satisfied. +// |MOJO_RESULT_FAILED_PRECONDITION| if it becomes known that none of the +// signals in |signals| can ever be satisfied (e.g., when waiting on one +// end of a message pipe and the other end is closed). +// +// If there are multiple waiters (on different threads, obviously) waiting on +// the same handle and signal, and that signal becomes satisfied, all waiters +// will be awoken. +MOJO_SYSTEM_EXPORT MojoResult +MojoWait(MojoHandle handle, + MojoHandleSignals signals, + MojoDeadline deadline, + struct MojoHandleSignalsState* signals_state); // Optional out. + +// Waits on |handles[0]|, ..., |handles[num_handles-1]| until: +// - (At least) one handle satisfies a signal indicated in its respective +// |signals[0]|, ..., |signals[num_handles-1]|. +// - It becomes known that no signal in some |signals[i]| will ever be +// satisfied. +// - |deadline| has passed. +// +// This means that |MojoWaitMany()| behaves as if |MojoWait()| were called on +// each handle/signals pair simultaneously, completing when the first +// |MojoWait()| would complete. +// +// See |MojoWait()| for more details about |deadline|. +// +// |result_index| (optional) is used to return the index of the handle that +// caused the call to return. For example, the index |i| (from 0 to +// |num_handles-1|) if |handle[i]| satisfies a signal from |signals[i]|. You +// must manually initialize this to a suitable sentinel value (e.g. -1) +// before you make this call because this value is not updated if there is +// no specific handle that causes the function to return. Pass null if you +// don't need this value to be returned. +// +// |signals_states| (optional) points to an array of size |num_handles| of +// MojoHandleSignalsState. See |MojoHandleSignalsState| for more details +// about the meaning of each array entry. This array is not an atomic +// snapshot. The array will be updated if the function does not return +// |MOJO_RESULT_INVALID_ARGUMENT| or |MOJO_RESULT_RESOURCE_EXHAUSTED|. +// +// Returns: +// |MOJO_RESULT_CANCELLED| if some |handle[i]| was closed (necessarily from +// another thread) during the wait. +// |MOJO_RESULT_RESOURCE_EXHAUSTED| if there are too many handles. The +// |signals_state| array is unchanged. +// |MOJO_RESULT_INVALID_ARGUMENT| if some |handle[i]| is not a valid handle +// (e.g., if it is zero or if it has already been closed). The +// |signals_state| array is unchanged. +// |MOJO_RESULT_DEADLINE_EXCEEDED| if the deadline has passed without any of +// handles satisfying any of its signals. +// |MOJO_RESULT_FAILED_PRECONDITION| if it is or becomes impossible that SOME +// |handle[i]| will ever satisfy any of the signals in |signals[i]|. +MOJO_SYSTEM_EXPORT MojoResult +MojoWaitMany(const MojoHandle* handles, + const MojoHandleSignals* signals, + uint32_t num_handles, + MojoDeadline deadline, + uint32_t* result_index, // Optional out + struct MojoHandleSignalsState* signals_states); // Optional out + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // MOJO_PUBLIC_C_SYSTEM_FUNCTIONS_H_ diff --git a/mojo/public/c/system/macros.h b/mojo/public/c/system/macros.h new file mode 100644 index 0000000..7f28e18 --- /dev/null +++ b/mojo/public/c/system/macros.h @@ -0,0 +1,79 @@ +// 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. + +#ifndef MOJO_PUBLIC_C_SYSTEM_MACROS_H_ +#define MOJO_PUBLIC_C_SYSTEM_MACROS_H_ + +#include <stddef.h> + +// Annotate a variable indicating it's okay if it's unused. +// Use like: +// int x = ...; +// MOJO_ALLOW_UNUSED_LOCAL(x); +#define MOJO_ALLOW_UNUSED_LOCAL(x) false ? (void)x : (void)0 + +// Annotate a function indicating that the caller must examine the return value. +// Use like: +// int foo() MOJO_WARN_UNUSED_RESULT; +// Note that it can only be used on the prototype, and not the definition. +#if defined(__GNUC__) +#define MOJO_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#else +#define MOJO_WARN_UNUSED_RESULT +#endif + +#ifdef __cplusplus +// Used to explicitly mark the return value of a function as unused. If you are +// really sure you don't want to do anything with the return value of a function +// that has been marked WARN_UNUSED_RESULT, wrap it with this. Example: +// +// scoped_ptr<MyType> my_var = ...; +// if (TakeOwnership(my_var.get()) == SUCCESS) +// mojo_ignore_result(my_var.release()); +// +template <typename T> +inline void mojo_ignore_result(const T&) { +} +#endif + +// Assert things at compile time. (|msg| should be a valid identifier name.) +// This macro is currently C++-only, but we want to use it in the C core.h. +// Use like: +// MOJO_STATIC_ASSERT(sizeof(Foo) == 12, "Foo has invalid size"); +#if defined(__cplusplus) +#define MOJO_STATIC_ASSERT(expr, msg) static_assert(expr, msg) +#else +#define MOJO_STATIC_ASSERT(expr, msg) +#endif + +// Like the C++11 |alignof| operator. +#if __cplusplus >= 201103L +#define MOJO_ALIGNOF(type) alignof(type) +#elif defined(__GNUC__) +#define MOJO_ALIGNOF(type) __alignof__(type) +#elif defined(_MSC_VER) +// The use of |sizeof| is to work around a bug in MSVC 2010 (see +// http://goo.gl/isH0C; supposedly fixed since then). +#define MOJO_ALIGNOF(type) (sizeof(type) - sizeof(type) + __alignof(type)) +#else +#error "Please define MOJO_ALIGNOF() for your compiler." +#endif + +// Specify the alignment of a |struct|, etc. +// Use like: +// struct MOJO_ALIGNAS(8) Foo { ... }; +// Unlike the C++11 |alignas()|, |alignment| must be an integer. It may not be a +// type, nor can it be an expression like |MOJO_ALIGNOF(type)| (due to the +// non-C++11 MSVS version). +#if __cplusplus >= 201103L +#define MOJO_ALIGNAS(alignment) alignas(alignment) +#elif defined(__GNUC__) +#define MOJO_ALIGNAS(alignment) __attribute__((aligned(alignment))) +#elif defined(_MSC_VER) +#define MOJO_ALIGNAS(alignment) __declspec(align(alignment)) +#else +#error "Please define MOJO_ALIGNAS() for your compiler." +#endif + +#endif // MOJO_PUBLIC_C_SYSTEM_MACROS_H_ diff --git a/mojo/public/c/system/main.h b/mojo/public/c/system/main.h new file mode 100644 index 0000000..65d0837 --- /dev/null +++ b/mojo/public/c/system/main.h @@ -0,0 +1,35 @@ +// 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. + +#ifndef MOJO_PUBLIC_C_SYSTEM_MAIN_H_ +#define MOJO_PUBLIC_C_SYSTEM_MAIN_H_ + +#include "mojo/public/c/system/types.h" + +// Implement MojoMain directly as the entry point for an application. +// +// MojoResult MojoMain(MojoHandle application_request) { +// ... +// } +// +// TODO(davemoore): Establish this as part of our SDK for third party mojo +// application writers. + +#if defined(__cplusplus) +extern "C" { +#endif + +#if defined(WIN32) +__declspec(dllexport) MojoResult + __cdecl MojoMain(MojoHandle application_request); +#else // !defined(WIN32) +__attribute__((visibility("default"))) MojoResult + MojoMain(MojoHandle service_provider_handle); +#endif // defined(WIN32) + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // MOJO_PUBLIC_C_SYSTEM_MAIN_H_ diff --git a/mojo/public/c/system/message_pipe.h b/mojo/public/c/system/message_pipe.h new file mode 100644 index 0000000..d42c3fc --- /dev/null +++ b/mojo/public/c/system/message_pipe.h @@ -0,0 +1,177 @@ +// 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. + +// This file contains types/constants and functions specific to message pipes. +// +// Note: This header should be compilable as C. + +#ifndef MOJO_PUBLIC_C_SYSTEM_MESSAGE_PIPE_H_ +#define MOJO_PUBLIC_C_SYSTEM_MESSAGE_PIPE_H_ + +#include "mojo/public/c/system/macros.h" +#include "mojo/public/c/system/system_export.h" +#include "mojo/public/c/system/types.h" + +// |MojoCreateMessagePipeOptions|: Used to specify creation parameters for a +// message pipe to |MojoCreateMessagePipe()|. +// |uint32_t struct_size|: Set to the size of the +// |MojoCreateMessagePipeOptions| struct. (Used to allow for future +// extensions.) +// |MojoCreateMessagePipeOptionsFlags flags|: Reserved for future use. +// |MOJO_CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE|: No flags; default mode. + +typedef uint32_t MojoCreateMessagePipeOptionsFlags; + +#ifdef __cplusplus +const MojoCreateMessagePipeOptionsFlags + MOJO_CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE = 0; +#else +#define MOJO_CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE \ + ((MojoCreateMessagePipeOptionsFlags)0) +#endif + +MOJO_STATIC_ASSERT(MOJO_ALIGNOF(int64_t) == 8, "int64_t has weird alignment"); +struct MOJO_ALIGNAS(8) MojoCreateMessagePipeOptions { + uint32_t struct_size; + MojoCreateMessagePipeOptionsFlags flags; +}; +MOJO_STATIC_ASSERT(sizeof(MojoCreateMessagePipeOptions) == 8, + "MojoCreateMessagePipeOptions has wrong size"); + +// |MojoWriteMessageFlags|: Used to specify different modes to +// |MojoWriteMessage()|. +// |MOJO_WRITE_MESSAGE_FLAG_NONE| - No flags; default mode. + +typedef uint32_t MojoWriteMessageFlags; + +#ifdef __cplusplus +const MojoWriteMessageFlags MOJO_WRITE_MESSAGE_FLAG_NONE = 0; +#else +#define MOJO_WRITE_MESSAGE_FLAG_NONE ((MojoWriteMessageFlags)0) +#endif + +// |MojoReadMessageFlags|: Used to specify different modes to +// |MojoReadMessage()|. +// |MOJO_READ_MESSAGE_FLAG_NONE| - No flags; default mode. +// |MOJO_READ_MESSAGE_FLAG_MAY_DISCARD| - If the message is unable to be read +// for whatever reason (e.g., the caller-supplied buffer is too small), +// discard the message (i.e., simply dequeue it). + +typedef uint32_t MojoReadMessageFlags; + +#ifdef __cplusplus +const MojoReadMessageFlags MOJO_READ_MESSAGE_FLAG_NONE = 0; +const MojoReadMessageFlags MOJO_READ_MESSAGE_FLAG_MAY_DISCARD = 1 << 0; +#else +#define MOJO_READ_MESSAGE_FLAG_NONE ((MojoReadMessageFlags)0) +#define MOJO_READ_MESSAGE_FLAG_MAY_DISCARD ((MojoReadMessageFlags)1 << 0) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// Note: See the comment in functions.h about the meaning of the "optional" +// label for pointer parameters. + +// Creates a message pipe, which is a bidirectional communication channel for +// framed data (i.e., messages). Messages can contain plain data and/or Mojo +// handles. +// +// |options| may be set to null for a message pipe with the default options. +// +// On success, |*message_pipe_handle0| and |*message_pipe_handle1| are set to +// handles for the two endpoints (ports) for the message pipe. +// +// Returns: +// |MOJO_RESULT_OK| on success. +// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g., +// |*options| is invalid). +// |MOJO_RESULT_RESOURCE_EXHAUSTED| if a process/system/quota/etc. limit has +// been reached. +MOJO_SYSTEM_EXPORT MojoResult MojoCreateMessagePipe( + const struct MojoCreateMessagePipeOptions* options, // Optional. + MojoHandle* message_pipe_handle0, // Out. + MojoHandle* message_pipe_handle1); // Out. + +// Writes a message to the message pipe endpoint given by |message_pipe_handle|, +// with message data specified by |bytes| of size |num_bytes| and attached +// handles specified by |handles| of count |num_handles|, and options specified +// by |flags|. If there is no message data, |bytes| may be null, in which case +// |num_bytes| must be zero. If there are no attached handles, |handles| may be +// null, in which case |num_handles| must be zero. +// +// If handles are attached, on success the handles will no longer be valid (the +// receiver will receive equivalent, but logically different, handles). Handles +// to be sent should not be in simultaneous use (e.g., on another thread). +// +// Returns: +// |MOJO_RESULT_OK| on success (i.e., the message was enqueued). +// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g., if +// |message_pipe_handle| is not a valid handle, or some of the +// requirements above are not satisfied). +// |MOJO_RESULT_RESOURCE_EXHAUSTED| if some system limit has been reached, or +// the number of handles to send is too large (TODO(vtl): reconsider the +// latter case). +// |MOJO_RESULT_FAILED_PRECONDITION| if the other endpoint has been closed. +// Note that closing an endpoint is not necessarily synchronous (e.g., +// across processes), so this function may succeed even if the other +// endpoint has been closed (in which case the message would be dropped). +// |MOJO_RESULT_UNIMPLEMENTED| if an unsupported flag was set in |*options|. +// |MOJO_RESULT_BUSY| if some handle to be sent is currently in use. +// +// TODO(vtl): Add a notion of capacity for message pipes, and return +// |MOJO_RESULT_SHOULD_WAIT| if the message pipe is full. +MOJO_SYSTEM_EXPORT MojoResult + MojoWriteMessage(MojoHandle message_pipe_handle, + const void* bytes, // Optional. + uint32_t num_bytes, + const MojoHandle* handles, // Optional. + uint32_t num_handles, + MojoWriteMessageFlags flags); + +// Reads the next message from a message pipe, or indicates the size of the +// message if it cannot fit in the provided buffers. The message will be read +// in its entirety or not at all; if it is not, it will remain enqueued unless +// the |MOJO_READ_MESSAGE_FLAG_MAY_DISCARD| flag was passed. At most one +// message will be consumed from the queue, and the return value will indicate +// whether a message was successfully read. +// +// |num_bytes| and |num_handles| are optional in/out parameters that on input +// must be set to the sizes of the |bytes| and |handles| arrays, and on output +// will be set to the actual number of bytes or handles contained in the +// message (even if the message was not retrieved due to being too large). +// Either |num_bytes| or |num_handles| may be null if the message is not +// expected to contain the corresponding type of data, but such a call would +// fail with |MOJO_RESULT_RESOURCE_EXHAUSTED| if the message in fact did +// contain that type of data. +// +// |bytes| and |handles| will receive the contents of the message, if it is +// retrieved. Either or both may be null, in which case the corresponding size +// parameter(s) must also be set to zero or passed as null. +// +// Returns: +// |MOJO_RESULT_OK| on success (i.e., a message was actually read). +// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid. +// |MOJO_RESULT_FAILED_PRECONDITION| if the other endpoint has been closed. +// |MOJO_RESULT_RESOURCE_EXHAUSTED| if the message was too large to fit in the +// provided buffer(s). The message will have been left in the queue or +// discarded, depending on flags. +// |MOJO_RESULT_SHOULD_WAIT| if no message was available to be read. +// +// TODO(vtl): Reconsider the |MOJO_RESULT_RESOURCE_EXHAUSTED| error code; should +// distinguish this from the hitting-system-limits case. +MOJO_SYSTEM_EXPORT MojoResult + MojoReadMessage(MojoHandle message_pipe_handle, + void* bytes, // Optional out. + uint32_t* num_bytes, // Optional in/out. + MojoHandle* handles, // Optional out. + uint32_t* num_handles, // Optional in/out. + MojoReadMessageFlags flags); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // MOJO_PUBLIC_C_SYSTEM_MESSAGE_PIPE_H_ diff --git a/mojo/public/c/system/system_export.h b/mojo/public/c/system/system_export.h new file mode 100644 index 0000000..bc3b459 --- /dev/null +++ b/mojo/public/c/system/system_export.h @@ -0,0 +1,33 @@ +// 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. + +#ifndef MOJO_PUBLIC_C_SYSTEM_SYSTEM_EXPORT_H_ +#define MOJO_PUBLIC_C_SYSTEM_SYSTEM_EXPORT_H_ + +#if defined(COMPONENT_BUILD) && defined(MOJO_USE_SYSTEM_IMPL) +#if defined(WIN32) + +#if defined(MOJO_SYSTEM_IMPLEMENTATION) +#define MOJO_SYSTEM_EXPORT __declspec(dllexport) +#else +#define MOJO_SYSTEM_EXPORT __declspec(dllimport) +#endif + +#else // !defined(WIN32) + +#if defined(MOJO_SYSTEM_IMPLEMENTATION) +#define MOJO_SYSTEM_EXPORT __attribute__((visibility("default"))) +#else +#define MOJO_SYSTEM_EXPORT +#endif + +#endif // defined(WIN32) + +#else // !defined(COMPONENT_BUILD) || !defined(MOJO_USE_SYSTEM_IMPL) + +#define MOJO_SYSTEM_EXPORT + +#endif // defined(COMPONENT_BUILD) && defined(MOJO_USE_SYSTEM_IMPL) + +#endif // MOJO_PUBLIC_C_SYSTEM_SYSTEM_EXPORT_H_ diff --git a/mojo/public/c/system/tests/BUILD.gn b/mojo/public/c/system/tests/BUILD.gn new file mode 100644 index 0000000..bb3cc5e --- /dev/null +++ b/mojo/public/c/system/tests/BUILD.gn @@ -0,0 +1,49 @@ +# 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. + +import("../../../mojo_sdk.gni") + +mojo_sdk_source_set("tests") { + testonly = true + + visibility = [ + "//mojo/public/cpp/system/tests:mojo_public_system_unittests", + "//mojo/public/cpp/system/tests:tests", + ] + + sources = [ + "core_unittest.cc", + "core_unittest_pure_c.c", + "macros_unittest.cc", + ] + + deps = [ + "//testing/gtest", + ] + + mojo_sdk_deps = [ + "mojo/public/c/environment", + "mojo/public/c/system", + ] +} + +mojo_sdk_source_set("perftests") { + testonly = true + + sources = [ + "core_perftest.cc", + ] + + deps = [ + "//testing/gtest", + ] + + mojo_sdk_deps = [ + "mojo/public/c/environment", + "mojo/public/cpp/environment:standalone", + "mojo/public/cpp/system", + "mojo/public/cpp/test_support:test_utils", + "mojo/public/cpp/utility", + ] +} diff --git a/mojo/public/c/system/tests/core_perftest.cc b/mojo/public/c/system/tests/core_perftest.cc new file mode 100644 index 0000000..3ee3015 --- /dev/null +++ b/mojo/public/c/system/tests/core_perftest.cc @@ -0,0 +1,325 @@ +// 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. + +// This tests the performance of the C API. + +#include "mojo/public/c/system/core.h" + +#include <assert.h> +#include <stdint.h> +#include <stdio.h> + +#include "mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/test_support/test_support.h" +#include "mojo/public/cpp/test_support/test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +// TODO(vtl): (here and below) crbug.com/342893 +#if !defined(WIN32) +#include <time.h> +#include "mojo/public/cpp/utility/thread.h" +#endif // !defined(WIN32) + +namespace { + +#if !defined(WIN32) +class MessagePipeWriterThread : public mojo::Thread { + public: + MessagePipeWriterThread(MojoHandle handle, uint32_t num_bytes) + : handle_(handle), num_bytes_(num_bytes), num_writes_(0) {} + ~MessagePipeWriterThread() override {} + + void Run() override { + char buffer[10000]; + assert(num_bytes_ <= sizeof(buffer)); + + // TODO(vtl): Should I throttle somehow? + for (;;) { + MojoResult result = MojoWriteMessage(handle_, buffer, num_bytes_, nullptr, + 0, MOJO_WRITE_MESSAGE_FLAG_NONE); + if (result == MOJO_RESULT_OK) { + num_writes_++; + continue; + } + + // We failed to write. + // Either |handle_| or its peer was closed. + assert(result == MOJO_RESULT_INVALID_ARGUMENT || + result == MOJO_RESULT_FAILED_PRECONDITION); + break; + } + } + + // Use only after joining the thread. + int64_t num_writes() const { return num_writes_; } + + private: + const MojoHandle handle_; + const uint32_t num_bytes_; + int64_t num_writes_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(MessagePipeWriterThread); +}; + +class MessagePipeReaderThread : public mojo::Thread { + public: + explicit MessagePipeReaderThread(MojoHandle handle) + : handle_(handle), num_reads_(0) {} + ~MessagePipeReaderThread() override {} + + void Run() override { + char buffer[10000]; + + for (;;) { + uint32_t num_bytes = static_cast<uint32_t>(sizeof(buffer)); + MojoResult result = MojoReadMessage(handle_, buffer, &num_bytes, nullptr, + nullptr, MOJO_READ_MESSAGE_FLAG_NONE); + if (result == MOJO_RESULT_OK) { + num_reads_++; + continue; + } + + if (result == MOJO_RESULT_SHOULD_WAIT) { + result = MojoWait(handle_, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_DEADLINE_INDEFINITE, nullptr); + if (result == MOJO_RESULT_OK) { + // Go to the top of the loop to read again. + continue; + } + } + + // We failed to read and possibly failed to wait. + // Either |handle_| or its peer was closed. + assert(result == MOJO_RESULT_INVALID_ARGUMENT || + result == MOJO_RESULT_FAILED_PRECONDITION); + break; + } + } + + // Use only after joining the thread. + int64_t num_reads() const { return num_reads_; } + + private: + const MojoHandle handle_; + int64_t num_reads_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(MessagePipeReaderThread); +}; +#endif // !defined(WIN32) + +class CorePerftest : public testing::Test { + public: + CorePerftest() : buffer_(nullptr), num_bytes_(0) {} + ~CorePerftest() override {} + + static void NoOp(void* /*closure*/) {} + + static void MessagePipe_CreateAndClose(void* closure) { + CorePerftest* self = static_cast<CorePerftest*>(closure); + MojoResult result = MojoCreateMessagePipe(nullptr, &self->h0_, &self->h1_); + MOJO_ALLOW_UNUSED_LOCAL(result); + assert(result == MOJO_RESULT_OK); + result = MojoClose(self->h0_); + assert(result == MOJO_RESULT_OK); + result = MojoClose(self->h1_); + assert(result == MOJO_RESULT_OK); + } + + static void MessagePipe_WriteAndRead(void* closure) { + CorePerftest* self = static_cast<CorePerftest*>(closure); + MojoResult result = + MojoWriteMessage(self->h0_, self->buffer_, self->num_bytes_, nullptr, 0, + MOJO_WRITE_MESSAGE_FLAG_NONE); + MOJO_ALLOW_UNUSED_LOCAL(result); + assert(result == MOJO_RESULT_OK); + uint32_t read_bytes = self->num_bytes_; + result = MojoReadMessage(self->h1_, self->buffer_, &read_bytes, nullptr, + nullptr, MOJO_READ_MESSAGE_FLAG_NONE); + assert(result == MOJO_RESULT_OK); + } + + static void MessagePipe_EmptyRead(void* closure) { + CorePerftest* self = static_cast<CorePerftest*>(closure); + MojoResult result = + MojoReadMessage(self->h0_, nullptr, nullptr, nullptr, nullptr, + MOJO_READ_MESSAGE_FLAG_MAY_DISCARD); + MOJO_ALLOW_UNUSED_LOCAL(result); + assert(result == MOJO_RESULT_SHOULD_WAIT); + } + + protected: +#if !defined(WIN32) + void DoMessagePipeThreadedTest(unsigned num_writers, + unsigned num_readers, + uint32_t num_bytes) { + static const int64_t kPerftestTimeMicroseconds = 3 * 1000000; + + assert(num_writers > 0); + assert(num_readers > 0); + + MojoResult result = MojoCreateMessagePipe(nullptr, &h0_, &h1_); + MOJO_ALLOW_UNUSED_LOCAL(result); + assert(result == MOJO_RESULT_OK); + + std::vector<MessagePipeWriterThread*> writers; + for (unsigned i = 0; i < num_writers; i++) + writers.push_back(new MessagePipeWriterThread(h0_, num_bytes)); + + std::vector<MessagePipeReaderThread*> readers; + for (unsigned i = 0; i < num_readers; i++) + readers.push_back(new MessagePipeReaderThread(h1_)); + + // Start time here, just before we fire off the threads. + const MojoTimeTicks start_time = MojoGetTimeTicksNow(); + + // Interleave the starts. + for (unsigned i = 0; i < num_writers || i < num_readers; i++) { + if (i < num_writers) + writers[i]->Start(); + if (i < num_readers) + readers[i]->Start(); + } + + Sleep(kPerftestTimeMicroseconds); + + // Close both handles to make writers and readers stop immediately. + result = MojoClose(h0_); + assert(result == MOJO_RESULT_OK); + result = MojoClose(h1_); + assert(result == MOJO_RESULT_OK); + + // Join everything. + for (unsigned i = 0; i < num_writers; i++) + writers[i]->Join(); + for (unsigned i = 0; i < num_readers; i++) + readers[i]->Join(); + + // Stop time here. + MojoTimeTicks end_time = MojoGetTimeTicksNow(); + + // Add up write and read counts, and destroy the threads. + int64_t num_writes = 0; + for (unsigned i = 0; i < num_writers; i++) { + num_writes += writers[i]->num_writes(); + delete writers[i]; + } + writers.clear(); + int64_t num_reads = 0; + for (unsigned i = 0; i < num_readers; i++) { + num_reads += readers[i]->num_reads(); + delete readers[i]; + } + readers.clear(); + + char sub_test_name[200]; + sprintf(sub_test_name, "%uw_%ur_%ubytes", num_writers, num_readers, + static_cast<unsigned>(num_bytes)); + mojo::test::LogPerfResult( + "MessagePipe_Threaded_Writes", sub_test_name, + 1000000.0 * static_cast<double>(num_writes) / (end_time - start_time), + "writes/second"); + mojo::test::LogPerfResult( + "MessagePipe_Threaded_Reads", sub_test_name, + 1000000.0 * static_cast<double>(num_reads) / (end_time - start_time), + "reads/second"); + } +#endif // !defined(WIN32) + + MojoHandle h0_; + MojoHandle h1_; + + void* buffer_; + uint32_t num_bytes_; + + private: +#if !defined(WIN32) + void Sleep(int64_t microseconds) { + struct timespec req = { + static_cast<time_t>(microseconds / 1000000), // Seconds. + static_cast<long>(microseconds % 1000000) * 1000L // Nanoseconds. + }; + int rv = nanosleep(&req, nullptr); + MOJO_ALLOW_UNUSED_LOCAL(rv); + assert(rv == 0); + } +#endif // !defined(WIN32) + + MOJO_DISALLOW_COPY_AND_ASSIGN(CorePerftest); +}; + +// A no-op test so we can compare performance. +TEST_F(CorePerftest, NoOp) { + mojo::test::IterateAndReportPerf("Iterate_NoOp", nullptr, &CorePerftest::NoOp, + this); +} + +TEST_F(CorePerftest, MessagePipe_CreateAndClose) { + mojo::test::IterateAndReportPerf("MessagePipe_CreateAndClose", nullptr, + &CorePerftest::MessagePipe_CreateAndClose, + this); +} + +TEST_F(CorePerftest, MessagePipe_WriteAndRead) { + MojoResult result = MojoCreateMessagePipe(nullptr, &h0_, &h1_); + MOJO_ALLOW_UNUSED_LOCAL(result); + assert(result == MOJO_RESULT_OK); + char buffer[10000] = {0}; + buffer_ = buffer; + num_bytes_ = 10u; + mojo::test::IterateAndReportPerf("MessagePipe_WriteAndRead", "10bytes", + &CorePerftest::MessagePipe_WriteAndRead, + this); + num_bytes_ = 100u; + mojo::test::IterateAndReportPerf("MessagePipe_WriteAndRead", "100bytes", + &CorePerftest::MessagePipe_WriteAndRead, + this); + num_bytes_ = 1000u; + mojo::test::IterateAndReportPerf("MessagePipe_WriteAndRead", "1000bytes", + &CorePerftest::MessagePipe_WriteAndRead, + this); + num_bytes_ = 10000u; + mojo::test::IterateAndReportPerf("MessagePipe_WriteAndRead", "10000bytes", + &CorePerftest::MessagePipe_WriteAndRead, + this); + result = MojoClose(h0_); + assert(result == MOJO_RESULT_OK); + result = MojoClose(h1_); + assert(result == MOJO_RESULT_OK); +} + +TEST_F(CorePerftest, MessagePipe_EmptyRead) { + MojoResult result = MojoCreateMessagePipe(nullptr, &h0_, &h1_); + MOJO_ALLOW_UNUSED_LOCAL(result); + assert(result == MOJO_RESULT_OK); + mojo::test::IterateAndReportPerf("MessagePipe_EmptyRead", nullptr, + &CorePerftest::MessagePipe_EmptyRead, this); + result = MojoClose(h0_); + assert(result == MOJO_RESULT_OK); + result = MojoClose(h1_); + assert(result == MOJO_RESULT_OK); +} + +#if !defined(WIN32) +TEST_F(CorePerftest, MessagePipe_Threaded) { + DoMessagePipeThreadedTest(1u, 1u, 100u); + DoMessagePipeThreadedTest(2u, 2u, 100u); + DoMessagePipeThreadedTest(3u, 3u, 100u); + DoMessagePipeThreadedTest(10u, 10u, 100u); + DoMessagePipeThreadedTest(10u, 1u, 100u); + DoMessagePipeThreadedTest(1u, 10u, 100u); + + // For comparison of overhead: + DoMessagePipeThreadedTest(1u, 1u, 10u); + // 100 was done above. + DoMessagePipeThreadedTest(1u, 1u, 1000u); + DoMessagePipeThreadedTest(1u, 1u, 10000u); + + DoMessagePipeThreadedTest(3u, 3u, 10u); + // 100 was done above. + DoMessagePipeThreadedTest(3u, 3u, 1000u); + DoMessagePipeThreadedTest(3u, 3u, 10000u); +} +#endif // !defined(WIN32) + +} // namespace diff --git a/mojo/public/c/system/tests/core_unittest.cc b/mojo/public/c/system/tests/core_unittest.cc new file mode 100644 index 0000000..de59a70 --- /dev/null +++ b/mojo/public/c/system/tests/core_unittest.cc @@ -0,0 +1,348 @@ +// 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. + +// This file tests the C API. + +#include "mojo/public/c/system/core.h" + +#include <string.h> + +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace { + +const MojoHandleSignals kSignalReadadableWritable = + MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE; + +const MojoHandleSignals kSignalAll = MOJO_HANDLE_SIGNAL_READABLE | + MOJO_HANDLE_SIGNAL_WRITABLE | + MOJO_HANDLE_SIGNAL_PEER_CLOSED; + +TEST(CoreTest, GetTimeTicksNow) { + const MojoTimeTicks start = MojoGetTimeTicksNow(); + EXPECT_NE(static_cast<MojoTimeTicks>(0), start) + << "MojoGetTimeTicksNow should return nonzero value"; +} + +// The only handle that's guaranteed to be invalid is |MOJO_HANDLE_INVALID|. +// Tests that everything that takes a handle properly recognizes it. +TEST(CoreTest, InvalidHandle) { + MojoHandle h0, h1; + MojoHandleSignals sig; + char buffer[10] = {0}; + uint32_t buffer_size; + void* write_pointer; + const void* read_pointer; + + // Close: + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(MOJO_HANDLE_INVALID)); + + // Wait: + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + MojoWait(MOJO_HANDLE_INVALID, ~MOJO_HANDLE_SIGNAL_NONE, 1000000, + nullptr)); + + h0 = MOJO_HANDLE_INVALID; + sig = ~MOJO_HANDLE_SIGNAL_NONE; + EXPECT_EQ( + MOJO_RESULT_INVALID_ARGUMENT, + MojoWaitMany(&h0, &sig, 1, MOJO_DEADLINE_INDEFINITE, nullptr, nullptr)); + + // Message pipe: + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + MojoWriteMessage(h0, buffer, 3, nullptr, 0, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + buffer_size = static_cast<uint32_t>(sizeof(buffer)); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + MojoReadMessage(h0, buffer, &buffer_size, nullptr, nullptr, + MOJO_READ_MESSAGE_FLAG_NONE)); + + // Data pipe: + buffer_size = static_cast<uint32_t>(sizeof(buffer)); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + MojoWriteData(h0, buffer, &buffer_size, MOJO_WRITE_DATA_FLAG_NONE)); + write_pointer = nullptr; + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + MojoBeginWriteData(h0, &write_pointer, &buffer_size, + MOJO_WRITE_DATA_FLAG_NONE)); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoEndWriteData(h0, 1)); + buffer_size = static_cast<uint32_t>(sizeof(buffer)); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + MojoReadData(h0, buffer, &buffer_size, MOJO_READ_DATA_FLAG_NONE)); + read_pointer = nullptr; + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + MojoBeginReadData(h0, &read_pointer, &buffer_size, + MOJO_READ_DATA_FLAG_NONE)); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoEndReadData(h0, 1)); + + // Shared buffer: + h1 = MOJO_HANDLE_INVALID; + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + MojoDuplicateBufferHandle(h0, nullptr, &h1)); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + MojoMapBuffer(h0, 0, 1, &write_pointer, MOJO_MAP_BUFFER_FLAG_NONE)); +} + +TEST(CoreTest, BasicMessagePipe) { + MojoHandle h0, h1; + MojoHandleSignals sig; + char buffer[10] = {0}; + uint32_t buffer_size; + + h0 = MOJO_HANDLE_INVALID; + h1 = MOJO_HANDLE_INVALID; + EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessagePipe(nullptr, &h0, &h1)); + EXPECT_NE(h0, MOJO_HANDLE_INVALID); + EXPECT_NE(h1, MOJO_HANDLE_INVALID); + + // Shouldn't be readable, we haven't written anything. + MojoHandleSignalsState state; + EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, + MojoWait(h0, MOJO_HANDLE_SIGNAL_READABLE, 0, &state)); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals); + EXPECT_EQ(kSignalAll, state.satisfiable_signals); + + // Should be writable. + EXPECT_EQ(MOJO_RESULT_OK, + MojoWait(h0, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &state)); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals); + EXPECT_EQ(kSignalAll, state.satisfiable_signals); + + // Last parameter is optional. + EXPECT_EQ(MOJO_RESULT_OK, + MojoWait(h0, MOJO_HANDLE_SIGNAL_WRITABLE, 0, nullptr)); + + // Try to read. + buffer_size = static_cast<uint32_t>(sizeof(buffer)); + EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, + MojoReadMessage(h0, buffer, &buffer_size, nullptr, nullptr, + MOJO_READ_MESSAGE_FLAG_NONE)); + + // Write to |h1|. + static const char kHello[] = "hello"; + buffer_size = static_cast<uint32_t>(sizeof(kHello)); + EXPECT_EQ(MOJO_RESULT_OK, MojoWriteMessage(h1, kHello, buffer_size, nullptr, + 0, MOJO_WRITE_MESSAGE_FLAG_NONE)); + + // |h0| should be readable. + uint32_t result_index = 1; + MojoHandleSignalsState states[1]; + sig = MOJO_HANDLE_SIGNAL_READABLE; + EXPECT_EQ(MOJO_RESULT_OK, MojoWaitMany(&h0, &sig, 1, MOJO_DEADLINE_INDEFINITE, + &result_index, states)); + + EXPECT_EQ(0u, result_index); + EXPECT_EQ(kSignalReadadableWritable, states[0].satisfied_signals); + EXPECT_EQ(kSignalAll, states[0].satisfiable_signals); + + // Read from |h0|. + buffer_size = static_cast<uint32_t>(sizeof(buffer)); + EXPECT_EQ(MOJO_RESULT_OK, + MojoReadMessage(h0, buffer, &buffer_size, nullptr, nullptr, + MOJO_READ_MESSAGE_FLAG_NONE)); + EXPECT_EQ(static_cast<uint32_t>(sizeof(kHello)), buffer_size); + EXPECT_STREQ(kHello, buffer); + + // |h0| should no longer be readable. + EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, + MojoWait(h0, MOJO_HANDLE_SIGNAL_READABLE, 10, &state)); + + EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals); + EXPECT_EQ(kSignalAll, state.satisfiable_signals); + + // Close |h0|. + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h0)); + + // |h1| should no longer be readable or writable. + EXPECT_EQ( + MOJO_RESULT_FAILED_PRECONDITION, + MojoWait(h1, MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, + 1000, &state)); + + EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfiable_signals); + + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h1)); +} + +// TODO(ncbray): enable these tests once NaCl supports the corresponding APIs. +#ifdef __native_client__ +#define MAYBE_BasicDataPipe DISABLED_BasicDataPipe +#define MAYBE_BasicSharedBuffer DISABLED_BasicSharedBuffer +#else +#define MAYBE_BasicDataPipe BasicDataPipe +#define MAYBE_BasicSharedBuffer BasicSharedBuffer +#endif + +TEST(CoreTest, MAYBE_BasicDataPipe) { + MojoHandle hp, hc; + MojoHandleSignals sig; + char buffer[20] = {0}; + uint32_t buffer_size; + void* write_pointer; + const void* read_pointer; + + hp = MOJO_HANDLE_INVALID; + hc = MOJO_HANDLE_INVALID; + EXPECT_EQ(MOJO_RESULT_OK, MojoCreateDataPipe(nullptr, &hp, &hc)); + EXPECT_NE(hp, MOJO_HANDLE_INVALID); + EXPECT_NE(hc, MOJO_HANDLE_INVALID); + + // The consumer |hc| shouldn't be readable. + MojoHandleSignalsState state; + EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, + MojoWait(hc, MOJO_HANDLE_SIGNAL_READABLE, 0, &state)); + + EXPECT_EQ(MOJO_HANDLE_SIGNAL_NONE, state.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + state.satisfiable_signals); + + // The producer |hp| should be writable. + EXPECT_EQ(MOJO_RESULT_OK, + MojoWait(hp, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &state)); + + EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + state.satisfiable_signals); + + // Try to read from |hc|. + buffer_size = static_cast<uint32_t>(sizeof(buffer)); + EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, + MojoReadData(hc, buffer, &buffer_size, MOJO_READ_DATA_FLAG_NONE)); + + // Try to begin a two-phase read from |hc|. + read_pointer = nullptr; + EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, + MojoBeginReadData(hc, &read_pointer, &buffer_size, + MOJO_READ_DATA_FLAG_NONE)); + + // Write to |hp|. + static const char kHello[] = "hello "; + // Don't include terminating null. + buffer_size = static_cast<uint32_t>(strlen(kHello)); + EXPECT_EQ(MOJO_RESULT_OK, MojoWriteData(hp, kHello, &buffer_size, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + + // |hc| should be(come) readable. + uint32_t result_index = 1; + MojoHandleSignalsState states[1]; + sig = MOJO_HANDLE_SIGNAL_READABLE; + EXPECT_EQ(MOJO_RESULT_OK, MojoWaitMany(&hc, &sig, 1, MOJO_DEADLINE_INDEFINITE, + &result_index, states)); + + EXPECT_EQ(0u, result_index); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, states[0].satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + states[0].satisfiable_signals); + + // Do a two-phase write to |hp|. + EXPECT_EQ(MOJO_RESULT_OK, MojoBeginWriteData(hp, &write_pointer, &buffer_size, + MOJO_WRITE_DATA_FLAG_NONE)); + static const char kWorld[] = "world"; + ASSERT_GE(buffer_size, sizeof(kWorld)); + // Include the terminating null. + memcpy(write_pointer, kWorld, sizeof(kWorld)); + EXPECT_EQ(MOJO_RESULT_OK, + MojoEndWriteData(hp, static_cast<uint32_t>(sizeof(kWorld)))); + + // Read one character from |hc|. + memset(buffer, 0, sizeof(buffer)); + buffer_size = 1; + EXPECT_EQ(MOJO_RESULT_OK, + MojoReadData(hc, buffer, &buffer_size, MOJO_READ_DATA_FLAG_NONE)); + + // Close |hp|. + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(hp)); + + // |hc| should still be readable. + EXPECT_EQ(MOJO_RESULT_OK, + MojoWait(hc, MOJO_HANDLE_SIGNAL_READABLE, 0, &state)); + + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + state.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + state.satisfiable_signals); + + // Do a two-phase read from |hc|. + read_pointer = nullptr; + EXPECT_EQ(MOJO_RESULT_OK, MojoBeginReadData(hc, &read_pointer, &buffer_size, + MOJO_READ_DATA_FLAG_NONE)); + ASSERT_LE(buffer_size, sizeof(buffer) - 1); + memcpy(&buffer[1], read_pointer, buffer_size); + EXPECT_EQ(MOJO_RESULT_OK, MojoEndReadData(hc, buffer_size)); + EXPECT_STREQ("hello world", buffer); + + // |hc| should no longer be readable. + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + MojoWait(hc, MOJO_HANDLE_SIGNAL_READABLE, 1000, &state)); + + EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfiable_signals); + + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(hc)); + + // TODO(vtl): Test the other way around -- closing the consumer should make + // the producer never-writable? +} + +TEST(CoreTest, MAYBE_BasicSharedBuffer) { + MojoHandle h0, h1; + void* pointer; + + // Create a shared buffer (|h0|). + h0 = MOJO_HANDLE_INVALID; + EXPECT_EQ(MOJO_RESULT_OK, MojoCreateSharedBuffer(nullptr, 100, &h0)); + EXPECT_NE(h0, MOJO_HANDLE_INVALID); + + // Map everything. + pointer = nullptr; + EXPECT_EQ(MOJO_RESULT_OK, + MojoMapBuffer(h0, 0, 100, &pointer, MOJO_MAP_BUFFER_FLAG_NONE)); + ASSERT_TRUE(pointer); + static_cast<char*>(pointer)[50] = 'x'; + + // Duplicate |h0| to |h1|. + h1 = MOJO_HANDLE_INVALID; + EXPECT_EQ(MOJO_RESULT_OK, MojoDuplicateBufferHandle(h0, nullptr, &h1)); + EXPECT_NE(h1, MOJO_HANDLE_INVALID); + + // Close |h0|. + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h0)); + + // The mapping should still be good. + static_cast<char*>(pointer)[51] = 'y'; + + // Unmap it. + EXPECT_EQ(MOJO_RESULT_OK, MojoUnmapBuffer(pointer)); + + // Map half of |h1|. + pointer = nullptr; + EXPECT_EQ(MOJO_RESULT_OK, + MojoMapBuffer(h1, 50, 50, &pointer, MOJO_MAP_BUFFER_FLAG_NONE)); + ASSERT_TRUE(pointer); + + // It should have what we wrote. + EXPECT_EQ('x', static_cast<char*>(pointer)[0]); + EXPECT_EQ('y', static_cast<char*>(pointer)[1]); + + // Unmap it. + EXPECT_EQ(MOJO_RESULT_OK, MojoUnmapBuffer(pointer)); + + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h1)); +} + +// Defined in core_unittest_pure_c.c. +extern "C" const char* MinimalCTest(void); + +// This checks that things actually work in C (not C++). +TEST(CoreTest, MinimalCTest) { + const char* failure = MinimalCTest(); + EXPECT_FALSE(failure) << failure; +} + +// TODO(vtl): Add multi-threaded tests. + +} // namespace +} // namespace mojo diff --git a/mojo/public/c/system/tests/core_unittest_pure_c.c b/mojo/public/c/system/tests/core_unittest_pure_c.c new file mode 100644 index 0000000..d5978a9 --- /dev/null +++ b/mojo/public/c/system/tests/core_unittest_pure_c.c @@ -0,0 +1,103 @@ +// 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. + +#ifdef __cplusplus +#error "This file should be compiled as C, not C++." +#endif + +#include <stddef.h> +#include <string.h> + +// Include all the header files that are meant to be compilable as C. Start with +// core.h, since it's the most important one. +#include "mojo/public/c/environment/async_waiter.h" +#include "mojo/public/c/system/core.h" +#include "mojo/public/c/system/macros.h" + +// The joys of the C preprocessor.... +#define STRINGIFY(x) #x +#define STRINGIFY2(x) STRINGIFY(x) +#define FAILURE(message) \ + __FILE__ "(" STRINGIFY2(__LINE__) "): Failure: " message + +// Poor man's gtest. +#define EXPECT_EQ(a, b) \ + do { \ + if ((a) != (b)) \ + return FAILURE(STRINGIFY(a) " != " STRINGIFY(b) " (expected ==)"); \ + } while (0) +#define EXPECT_NE(a, b) \ + do { \ + if ((a) == (b)) \ + return FAILURE(STRINGIFY(a) " == " STRINGIFY(b) " (expected !=)"); \ + } while (0) + +// This function exists mainly to be compiled and linked. We do some cursory +// checks and call it from a unit test, to make sure that link problems aren't +// missed due to deadstripping. Returns null on success and a string on failure +// (describing the failure). +const char* MinimalCTest(void) { + // MSVS before 2013 *really* only supports C90: All variables must be declared + // at the top. (MSVS 2013 is more reasonable.) + MojoTimeTicks ticks; + MojoHandle handle0, handle1; + MojoHandleSignals signals; + const char kHello[] = "hello"; + char buffer[200] = {0}; + uint32_t num_bytes; + + ticks = MojoGetTimeTicksNow(); + EXPECT_NE(ticks, 0); + + handle0 = MOJO_HANDLE_INVALID; + EXPECT_NE(MOJO_RESULT_OK, MojoClose(handle0)); + + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + MojoWait(handle0, ~MOJO_HANDLE_SIGNAL_NONE, + MOJO_DEADLINE_INDEFINITE, NULL)); + + handle1 = MOJO_HANDLE_INVALID; + EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessagePipe(NULL, &handle0, &handle1)); + + signals = MOJO_HANDLE_SIGNAL_READABLE; + uint32_t result_index = 123; + struct MojoHandleSignalsState states[1]; + EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, + MojoWaitMany(&handle0, &signals, 1, 1, &result_index, states)); + + // "Deadline exceeded" doesn't apply to a single handle, so this should leave + // |result_index| untouched. + EXPECT_EQ(123u, result_index); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, states[0].satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE | + MOJO_HANDLE_SIGNAL_PEER_CLOSED, + states[0].satisfiable_signals); + + EXPECT_EQ(MOJO_RESULT_OK, + MojoWriteMessage(handle0, kHello, (uint32_t)sizeof(kHello), NULL, + 0u, MOJO_WRITE_DATA_FLAG_NONE)); + + struct MojoHandleSignalsState state; + EXPECT_EQ(MOJO_RESULT_OK, MojoWait(handle1, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_DEADLINE_INDEFINITE, &state)); + + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, + state.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE | + MOJO_HANDLE_SIGNAL_PEER_CLOSED, + state.satisfiable_signals); + + num_bytes = (uint32_t)sizeof(buffer); + EXPECT_EQ(MOJO_RESULT_OK, MojoReadMessage(handle1, buffer, &num_bytes, NULL, + NULL, MOJO_READ_MESSAGE_FLAG_NONE)); + EXPECT_EQ((uint32_t)sizeof(kHello), num_bytes); + EXPECT_EQ(0, memcmp(buffer, kHello, sizeof(kHello))); + + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(handle0)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(handle1)); + + // TODO(vtl): data pipe + + return NULL; +} diff --git a/mojo/public/c/system/tests/macros_unittest.cc b/mojo/public/c/system/tests/macros_unittest.cc new file mode 100644 index 0000000..b22e124 --- /dev/null +++ b/mojo/public/c/system/tests/macros_unittest.cc @@ -0,0 +1,84 @@ +// 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. + +// This file tests the C Mojo system macros and consists of "positive" tests, +// i.e., those verifying that things work (without compile errors, or even +// warnings if warnings are treated as errors). +// TODO(vtl): Fix no-compile tests (which are all disabled; crbug.com/105388) +// and write some "negative" tests. + +#include "mojo/public/c/system/macros.h" + +#include <assert.h> +#include <stdint.h> +#include <stdlib.h> + +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace { + +TEST(MacrosTest, AllowUnused) { + // Test that no warning/error is issued even though |x| is unused. + int x = 123; + MOJO_ALLOW_UNUSED_LOCAL(x); +} + +int MustUseReturnedResult() MOJO_WARN_UNUSED_RESULT; +int MustUseReturnedResult() { + return 456; +} + +TEST(MacrosTest, WarnUnusedResult) { + if (!MustUseReturnedResult()) + abort(); +} + +// First test |MOJO_STATIC_ASSERT()| in a global scope. +MOJO_STATIC_ASSERT(sizeof(int64_t) == 2 * sizeof(int32_t), + "Bad static_assert() failure in global scope"); + +TEST(MacrosTest, CompileAssert) { + // Then in a local scope. + MOJO_STATIC_ASSERT(sizeof(int32_t) == 2 * sizeof(int16_t), + "Bad static_assert() failure"); +} + +TEST(MacrosTest, Alignof) { + // Strictly speaking, this isn't a portable test, but I think it'll pass on + // all the platforms we currently support. + EXPECT_EQ(1u, MOJO_ALIGNOF(char)); + EXPECT_EQ(4u, MOJO_ALIGNOF(int32_t)); + EXPECT_EQ(8u, MOJO_ALIGNOF(int64_t)); + EXPECT_EQ(8u, MOJO_ALIGNOF(double)); +} + +// These structs are used in the Alignas test. Define them globally to avoid +// MSVS warnings/errors. +#if defined(_MSC_VER) +#pragma warning(push) +// Disable the warning "structure was padded due to __declspec(align())". +#pragma warning(disable : 4324) +#endif +struct MOJO_ALIGNAS(1) StructAlignas1 { + char x; +}; +struct MOJO_ALIGNAS(4) StructAlignas4 { + char x; +}; +struct MOJO_ALIGNAS(8) StructAlignas8 { + char x; +}; +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +TEST(MacrosTest, Alignas) { + EXPECT_EQ(1u, MOJO_ALIGNOF(StructAlignas1)); + EXPECT_EQ(4u, MOJO_ALIGNOF(StructAlignas4)); + EXPECT_EQ(8u, MOJO_ALIGNOF(StructAlignas8)); +} + +} // namespace +} // namespace mojo diff --git a/mojo/public/c/system/types.h b/mojo/public/c/system/types.h new file mode 100644 index 0000000..4574d74 --- /dev/null +++ b/mojo/public/c/system/types.h @@ -0,0 +1,185 @@ +// 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. + +// This file contains types and constants/macros common to different Mojo system +// APIs. +// +// Note: This header should be compilable as C. + +#ifndef MOJO_PUBLIC_C_SYSTEM_TYPES_H_ +#define MOJO_PUBLIC_C_SYSTEM_TYPES_H_ + +#include <stdint.h> + +#include "mojo/public/c/system/macros.h" + +// TODO(vtl): Notes: Use of undefined flags will lead to undefined behavior +// (typically they'll be ignored), not necessarily an error. + +// |MojoTimeTicks|: A time delta, in microseconds, the meaning of which is +// source-dependent. + +typedef int64_t MojoTimeTicks; + +// |MojoHandle|: Handles to Mojo objects. +// |MOJO_HANDLE_INVALID| - A value that is never a valid handle. + +typedef uint32_t MojoHandle; + +#ifdef __cplusplus +const MojoHandle MOJO_HANDLE_INVALID = 0; +#else +#define MOJO_HANDLE_INVALID ((MojoHandle)0) +#endif + +// |MojoResult|: Result codes for Mojo operations. The only success code is zero +// (|MOJO_RESULT_OK|); all non-zero values should be considered as error/failure +// codes (even if the value is not recognized). +// |MOJO_RESULT_OK| - Not an error; returned on success. +// |MOJO_RESULT_CANCELLED| - Operation was cancelled, typically by the caller. +// |MOJO_RESULT_UNKNOWN| - Unknown error (e.g., if not enough information is +// available for a more specific error). +// |MOJO_RESULT_INVALID_ARGUMENT| - Caller specified an invalid argument. This +// differs from |MOJO_RESULT_FAILED_PRECONDITION| in that the former +// indicates arguments that are invalid regardless of the state of the +// system. +// |MOJO_RESULT_DEADLINE_EXCEEDED| - Deadline expired before the operation +// could complete. +// |MOJO_RESULT_NOT_FOUND| - Some requested entity was not found (i.e., does +// not exist). +// |MOJO_RESULT_ALREADY_EXISTS| - Some entity or condition that we attempted +// to create already exists. +// |MOJO_RESULT_PERMISSION_DENIED| - The caller does not have permission to +// for the operation (use |MOJO_RESULT_RESOURCE_EXHAUSTED| for rejections +// caused by exhausting some resource instead). +// |MOJO_RESULT_RESOURCE_EXHAUSTED| - Some resource required for the call +// (possibly some quota) has been exhausted. +// |MOJO_RESULT_FAILED_PRECONDITION| - The system is not in a state required +// for the operation (use this if the caller must do something to rectify +// the state before retrying). +// |MOJO_RESULT_ABORTED| - The operation was aborted by the system, possibly +// due to a concurrency issue (use this if the caller may retry at a +// higher level). +// |MOJO_RESULT_OUT_OF_RANGE| - The operation was attempted past the valid +// range. Unlike |MOJO_RESULT_INVALID_ARGUMENT|, this indicates that the +// operation may be/become valid depending on the system state. (This +// error is similar to |MOJO_RESULT_FAILED_PRECONDITION|, but is more +// specific.) +// |MOJO_RESULT_UNIMPLEMENTED| - The operation is not implemented, supported, +// or enabled. +// |MOJO_RESULT_INTERNAL| - Internal error: this should never happen and +// indicates that some invariant expected by the system has been broken. +// |MOJO_RESULT_UNAVAILABLE| - The operation is (temporarily) currently +// unavailable. The caller may simply retry the operation (possibly with a +// backoff). +// |MOJO_RESULT_DATA_LOSS| - Unrecoverable data loss or corruption. +// |MOJO_RESULT_BUSY| - One of the resources involved is currently being used +// (possibly on another thread) in a way that prevents the current +// operation from proceeding, e.g., if the other operation may result in +// the resource being invalidated. +// |MOJO_RESULT_SHOULD_WAIT| - The request cannot currently be completed +// (e.g., if the data requested is not yet available). The caller should +// wait for it to be feasible using |MojoWait()| or |MojoWaitMany()|. +// +// The codes from |MOJO_RESULT_OK| to |MOJO_RESULT_DATA_LOSS| come from +// Google3's canonical error codes. +// +// TODO(vtl): Add a |MOJO_RESULT_UNSATISFIABLE|? + +typedef uint32_t MojoResult; + +#ifdef __cplusplus +const MojoResult MOJO_RESULT_OK = 0; +const MojoResult MOJO_RESULT_CANCELLED = 1; +const MojoResult MOJO_RESULT_UNKNOWN = 2; +const MojoResult MOJO_RESULT_INVALID_ARGUMENT = 3; +const MojoResult MOJO_RESULT_DEADLINE_EXCEEDED = 4; +const MojoResult MOJO_RESULT_NOT_FOUND = 5; +const MojoResult MOJO_RESULT_ALREADY_EXISTS = 6; +const MojoResult MOJO_RESULT_PERMISSION_DENIED = 7; +const MojoResult MOJO_RESULT_RESOURCE_EXHAUSTED = 8; +const MojoResult MOJO_RESULT_FAILED_PRECONDITION = 9; +const MojoResult MOJO_RESULT_ABORTED = 10; +const MojoResult MOJO_RESULT_OUT_OF_RANGE = 11; +const MojoResult MOJO_RESULT_UNIMPLEMENTED = 12; +const MojoResult MOJO_RESULT_INTERNAL = 13; +const MojoResult MOJO_RESULT_UNAVAILABLE = 14; +const MojoResult MOJO_RESULT_DATA_LOSS = 15; +const MojoResult MOJO_RESULT_BUSY = 16; +const MojoResult MOJO_RESULT_SHOULD_WAIT = 17; +#else +#define MOJO_RESULT_OK ((MojoResult)0) +#define MOJO_RESULT_CANCELLED ((MojoResult)1) +#define MOJO_RESULT_UNKNOWN ((MojoResult)2) +#define MOJO_RESULT_INVALID_ARGUMENT ((MojoResult)3) +#define MOJO_RESULT_DEADLINE_EXCEEDED ((MojoResult)4) +#define MOJO_RESULT_NOT_FOUND ((MojoResult)5) +#define MOJO_RESULT_ALREADY_EXISTS ((MojoResult)6) +#define MOJO_RESULT_PERMISSION_DENIED ((MojoResult)7) +#define MOJO_RESULT_RESOURCE_EXHAUSTED ((MojoResult)8) +#define MOJO_RESULT_FAILED_PRECONDITION ((MojoResult)9) +#define MOJO_RESULT_ABORTED ((MojoResult)10) +#define MOJO_RESULT_OUT_OF_RANGE ((MojoResult)11) +#define MOJO_RESULT_UNIMPLEMENTED ((MojoResult)12) +#define MOJO_RESULT_INTERNAL ((MojoResult)13) +#define MOJO_RESULT_UNAVAILABLE ((MojoResult)14) +#define MOJO_RESULT_DATA_LOSS ((MojoResult)15) +#define MOJO_RESULT_BUSY ((MojoResult)16) +#define MOJO_RESULT_SHOULD_WAIT ((MojoResult)17) +#endif + +// |MojoDeadline|: Used to specify deadlines (timeouts), in microseconds (except +// for |MOJO_DEADLINE_INDEFINITE|). +// |MOJO_DEADLINE_INDEFINITE| - Used to indicate "forever". + +typedef uint64_t MojoDeadline; + +#ifdef __cplusplus +const MojoDeadline MOJO_DEADLINE_INDEFINITE = static_cast<MojoDeadline>(-1); +#else +#define MOJO_DEADLINE_INDEFINITE ((MojoDeadline) - 1) +#endif + +// |MojoHandleSignals|: Used to specify signals that can be waited on for a +// handle (and which can be triggered), e.g., the ability to read or write to +// the handle. +// |MOJO_HANDLE_SIGNAL_NONE| - No flags. |MojoWait()|, etc. will return +// |MOJO_RESULT_FAILED_PRECONDITION| if you attempt to wait on this. +// |MOJO_HANDLE_SIGNAL_READABLE| - Can read (e.g., a message) from the handle. +// |MOJO_HANDLE_SIGNAL_WRITABLE| - Can write (e.g., a message) to the handle. +// |MOJO_HANDLE_SIGNAL_PEER_CLOSED| - The peer handle is closed. + +typedef uint32_t MojoHandleSignals; + +#ifdef __cplusplus +const MojoHandleSignals MOJO_HANDLE_SIGNAL_NONE = 0; +const MojoHandleSignals MOJO_HANDLE_SIGNAL_READABLE = 1 << 0; +const MojoHandleSignals MOJO_HANDLE_SIGNAL_WRITABLE = 1 << 1; +const MojoHandleSignals MOJO_HANDLE_SIGNAL_PEER_CLOSED = 1 << 2; +#else +#define MOJO_HANDLE_SIGNAL_NONE ((MojoHandleSignals)0) +#define MOJO_HANDLE_SIGNAL_READABLE ((MojoHandleSignals)1 << 0) +#define MOJO_HANDLE_SIGNAL_WRITABLE ((MojoHandleSignals)1 << 1) +#define MOJO_HANDLE_SIGNAL_PEER_CLOSED ((MojoHandleSignals)1 << 2) +#endif + +// |MojoHandleSignalsState|: Returned by wait functions to indicate the +// signaling state of handles. Members are as follows: +// - |satisfied signals|: Bitmask of signals that were satisfied at some time +// before the call returned. +// - |satisfiable signals|: These are the signals that are possible to +// satisfy. For example, if the return value was +// |MOJO_RESULT_FAILED_PRECONDITION|, you can use this field to +// determine which, if any, of the signals can still be satisfied. +// Note: This struct is not extensible (and only has 32-bit quantities), so it's +// 32-bit-aligned. +MOJO_STATIC_ASSERT(MOJO_ALIGNOF(int32_t) == 4, "int32_t has weird alignment"); +struct MOJO_ALIGNAS(4) MojoHandleSignalsState { + MojoHandleSignals satisfied_signals; + MojoHandleSignals satisfiable_signals; +}; +MOJO_STATIC_ASSERT(sizeof(MojoHandleSignalsState) == 8, + "MojoHandleSignalsState has wrong size"); + +#endif // MOJO_PUBLIC_C_SYSTEM_TYPES_H_ diff --git a/mojo/public/c/test_support/BUILD.gn b/mojo/public/c/test_support/BUILD.gn new file mode 100644 index 0000000..3b11a60 --- /dev/null +++ b/mojo/public/c/test_support/BUILD.gn @@ -0,0 +1,25 @@ +# 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. + +# GYP version: mojo/public/mojo_public.gyp:mojo_test_support +shared_library("test_support") { + output_name = "mojo_test_support" + + defines = [ "MOJO_TEST_SUPPORT_IMPLEMENTATION" ] + + sources = [ + "test_support.h", + "test_support_export.h", + + # TODO(vtl): Convert this to thunks http://crbug.com/386799 + "../../tests/test_support_private.cc", + "../../tests/test_support_private.h", + ] + + if (is_mac) { + # TODO(GYP) + # # Make it a run-path dependent library. + # 'DYLIB_INSTALL_NAME_BASE': '@loader_path', + } +} diff --git a/mojo/public/c/test_support/test_support.h b/mojo/public/c/test_support/test_support.h new file mode 100644 index 0000000..73a2eef --- /dev/null +++ b/mojo/public/c/test_support/test_support.h @@ -0,0 +1,55 @@ +// 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. + +#ifndef MOJO_PUBLIC_C_TEST_SUPPORT_TEST_SUPPORT_H_ +#define MOJO_PUBLIC_C_TEST_SUPPORT_TEST_SUPPORT_H_ + +// Note: This header should be compilable as C. + +#include <stdio.h> + +#include "mojo/public/c/test_support/test_support_export.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// |sub_test_name| is optional. If not null, it usually describes one particular +// configuration of the test. For example, if |test_name| is "TestPacketRate", +// |sub_test_name| could be "100BytesPerPacket". +// When the perf data is visualized by the performance dashboard, data with +// different |sub_test_name|s (but the same |test_name|) are depicted as +// different traces on the same chart. +MOJO_TEST_SUPPORT_EXPORT void MojoTestSupportLogPerfResult( + const char* test_name, + const char* sub_test_name, + double value, + const char* units); + +// Opens a "/"-delimited file path relative to the source root. +MOJO_TEST_SUPPORT_EXPORT FILE* MojoTestSupportOpenSourceRootRelativeFile( + const char* source_root_relative_path); + +// Enumerates a "/"-delimited directory path relative to the source root. +// Returns only regular files. The return value is a heap-allocated array of +// heap-allocated strings. Each must be free'd separately. +// +// The return value is built like so: +// +// char** rv = (char**) calloc(N + 1, sizeof(char*)); +// rv[0] = strdup("a"); +// rv[1] = strdup("b"); +// rv[2] = strdup("c"); +// ... +// rv[N] = NULL; +// +MOJO_TEST_SUPPORT_EXPORT +char** MojoTestSupportEnumerateSourceRootRelativeDirectory( + const char* source_root_relative_path); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // MOJO_PUBLIC_C_TEST_SUPPORT_TEST_SUPPORT_H_ diff --git a/mojo/public/c/test_support/test_support_export.h b/mojo/public/c/test_support/test_support_export.h new file mode 100644 index 0000000..e22a9e3 --- /dev/null +++ b/mojo/public/c/test_support/test_support_export.h @@ -0,0 +1,26 @@ +// 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. + +#ifndef MOJO_PUBLIC_C_TEST_SUPPORT_TEST_SUPPORT_EXPORT_H_ +#define MOJO_PUBLIC_C_TEST_SUPPORT_TEST_SUPPORT_EXPORT_H_ + +#if defined(WIN32) + +#if defined(MOJO_TEST_SUPPORT_IMPLEMENTATION) +#define MOJO_TEST_SUPPORT_EXPORT __declspec(dllexport) +#else +#define MOJO_TEST_SUPPORT_EXPORT __declspec(dllimport) +#endif + +#else // !defined(WIN32) + +#if defined(MOJO_TEST_SUPPORT_IMPLEMENTATION) +#define MOJO_TEST_SUPPORT_EXPORT __attribute__((visibility("default"))) +#else +#define MOJO_TEST_SUPPORT_EXPORT +#endif + +#endif // defined(WIN32) + +#endif // MOJO_PUBLIC_C_TEST_SUPPORT_TEST_SUPPORT_EXPORT_H_ diff --git a/mojo/public/cpp/README.md b/mojo/public/cpp/README.md new file mode 100644 index 0000000..4404c24 --- /dev/null +++ b/mojo/public/cpp/README.md @@ -0,0 +1,71 @@ +Mojo Public C++ API +=================== + +This directory contains C++ language bindings for the Mojo Public API. + +A number of subdirectories provide wrappers for the lower-level C APIs (in +subdirectories of the same name, under mojo/public/c/). Typically, these +wrappers provide increased convenience and/or type-safety. + +Other subdirectories provide support (static) libraries of various sorts. In +this case, the organization is to have the public interface for the library +defined in header files in the subdirectory itself and the implementation of the +library at a lower level, under a lib (sub)subdirectory. A developer should be +able to substitute their own implementation of any such support library, and +expect other support libraries, which may depend on that library, to work +properly. + +Bindings +-------- + +The bindings/ subdirectory contains a support (static) library needed by the +code generated by the bindings generator tool (in mojo/public/tools/bindings/), +which translates Mojo IDL (.mojom) files into idiomatic C++ (among other +languages). + +This library depends on the Environment library. + +Environment +----------- + +The environment/ subdirectory contains a support (static) library that +represents shared state needed to support the Bindings and GLES2 libraries. + +This library depends on the Utility library. + + +GLES2 +----- + +The gles2/ subdirectory contains C++ wrappers (and some additional helpers) of +the API defined in mojo/public/c/gles2/ (which provides access to GLES2). + +These wrappers depend on the Environment library. + +Shell +----- + +The shell/ subdirectory contains a support (static) library that aids in writing +Mojo applications and interacting with the Shell service. + +System +------ + +The system/ subdirectory contains C++ wrappers (and some additional helpers) of +the API defined in mojo/public/c/system/, which defines the basic, "core" API, +especially used to communicate with Mojo services. + +Test Support +------------ + +The test_support/ subdirectory contains C++ wrappers of the test-only API +defined in mojo/public/c/test_support/. It is not meant for general use by Mojo +applications. + +Utility +------- + +The utility/ subdirectory contains a support (static) library that provides +various basic functionality. Most notably, it provides an implementation of a +RunLoop based on MojoWaitMany() that applications may use as the basis for +asynchronous message processing. diff --git a/mojo/public/cpp/bindings/BUILD.gn b/mojo/public/cpp/bindings/BUILD.gn new file mode 100644 index 0000000..89679f2 --- /dev/null +++ b/mojo/public/cpp/bindings/BUILD.gn @@ -0,0 +1,95 @@ +# 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. + +import("../../mojo_sdk.gni") + +mojo_sdk_source_set("bindings") { + sources = [ + "array.h", + "associated_interface_ptr_info.h", + "associated_interface_request.h", + "binding.h", + "interface_ptr.h", + "interface_ptr_info.h", + "interface_request.h", + "lib/array_internal.cc", + "lib/array_internal.h", + "lib/array_serialization.h", + "lib/bindings_internal.h", + "lib/bindings_serialization.cc", + "lib/bindings_serialization.h", + "lib/bounds_checker.cc", + "lib/bounds_checker.h", + "lib/buffer.h", + "lib/connector.cc", + "lib/connector.h", + "lib/control_message_handler.cc", + "lib/control_message_handler.h", + "lib/control_message_proxy.cc", + "lib/control_message_proxy.h", + "lib/filter_chain.cc", + "lib/filter_chain.h", + "lib/fixed_buffer.cc", + "lib/fixed_buffer.h", + "lib/interface_ptr_internal.h", + "lib/map_data_internal.h", + "lib/map_internal.h", + "lib/map_serialization.h", + "lib/message.cc", + "lib/message_builder.cc", + "lib/message_builder.h", + "lib/message_filter.cc", + "lib/message_header_validator.cc", + "lib/message_header_validator.h", + "lib/message_internal.h", + "lib/no_interface.cc", + "lib/router.cc", + "lib/router.h", + "lib/string_serialization.cc", + "lib/string_serialization.h", + "lib/union_accessor.h", + "lib/validate_params.h", + "lib/validation_errors.cc", + "lib/validation_errors.h", + "lib/validation_util.cc", + "lib/validation_util.h", + "lib/value_traits.h", + "map.h", + "message.h", + "message_filter.h", + "no_interface.h", + "string.h", + "strong_binding.h", + "struct_ptr.h", + "type_converter.h", + ] + + public_deps = [ + ":callback", + ] + + mojo_sdk_public_deps = [ + "mojo/public/cpp/system", + ] + + mojo_sdk_deps = [ + "mojo/public/cpp/environment", + "mojo/public/interfaces/bindings:bindings_cpp_sources", + ] +} + +mojo_sdk_source_set("callback") { + sources = [ + "callback.h", + "lib/callback_internal.h", + "lib/shared_data.h", + "lib/shared_ptr.h", + "lib/template_util.h", + "lib/thread_checker.h", + "lib/thread_checker_posix.cc", + "lib/thread_checker_posix.h", + ] + + mojo_sdk_deps = [ "mojo/public/cpp/system" ] +} diff --git a/mojo/public/cpp/bindings/array.h b/mojo/public/cpp/bindings/array.h new file mode 100644 index 0000000..f7d3921 --- /dev/null +++ b/mojo/public/cpp/bindings/array.h @@ -0,0 +1,249 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_ARRAY_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_ARRAY_H_ + +#include <string.h> + +#include <algorithm> +#include <set> +#include <string> +#include <vector> + +#include "mojo/public/cpp/bindings/lib/array_internal.h" +#include "mojo/public/cpp/bindings/lib/bindings_internal.h" +#include "mojo/public/cpp/bindings/lib/template_util.h" +#include "mojo/public/cpp/bindings/lib/value_traits.h" +#include "mojo/public/cpp/bindings/type_converter.h" + +namespace mojo { + +// Represents a moveable array with contents of type |T|. The array can be null, +// meaning that no value has been assigned to it. Null is distinct from empty. +template <typename T> +class Array { + MOJO_MOVE_ONLY_TYPE(Array) + public: + typedef internal::ArrayTraits<T, internal::IsMoveOnlyType<T>::value> Traits; + typedef typename Traits::ConstRefType ConstRefType; + typedef typename Traits::RefType RefType; + typedef typename Traits::StorageType StorageType; + typedef typename Traits::ForwardType ForwardType; + + typedef internal::Array_Data<typename internal::WrapperTraits<T>::DataType> + Data_; + + // Constructs a new array that is null. + Array() : is_null_(true) {} + + // Constructs a new non-null array of the specified size. The elements will + // be value-initialized (meaning that they will be initialized by their + // default constructor, if any, or else zero-initialized). + explicit Array(size_t size) : vec_(size), is_null_(false) { + Traits::Initialize(&vec_); + } + ~Array() { Traits::Finalize(&vec_); } + + // Moves the contents of |other| into this array. + Array(Array&& other) : is_null_(true) { Take(&other); } + Array& operator=(Array&& other) { + Take(&other); + return *this; + } + + // Creates a non-null array of the specified size. The elements will be + // value-initialized (meaning that they will be initialized by their default + // constructor, if any, or else zero-initialized). + static Array New(size_t size) { return Array(size).Pass(); } + + // Creates a new array with a copy of the contents of |other|. + template <typename U> + static Array From(const U& other) { + return TypeConverter<Array, U>::Convert(other); + } + + // Copies the contents of this array to a new object of type |U|. + template <typename U> + U To() const { + return TypeConverter<U, Array>::Convert(*this); + } + + // Resets the contents of this array back to null. + void reset() { + if (!vec_.empty()) { + Traits::Finalize(&vec_); + vec_.clear(); + } + is_null_ = true; + } + + // Indicates whether the array is null (which is distinct from empty). + bool is_null() const { return is_null_; } + + // Returns a reference to the first element of the array. Calling this on a + // null or empty array causes undefined behavior. + ConstRefType front() const { return vec_.front(); } + RefType front() { return vec_.front(); } + + // Returns the size of the array, which will be zero if the array is null. + size_t size() const { return vec_.size(); } + + // Returns a reference to the element at zero-based |offset|. Calling this on + // an array with size less than |offset|+1 causes undefined behavior. + ConstRefType at(size_t offset) const { return Traits::at(&vec_, offset); } + ConstRefType operator[](size_t offset) const { return at(offset); } + RefType at(size_t offset) { return Traits::at(&vec_, offset); } + RefType operator[](size_t offset) { return at(offset); } + + // Pushes |value| onto the back of the array. If this array was null, it will + // become non-null with a size of 1. + void push_back(ForwardType value) { + is_null_ = false; + Traits::PushBack(&vec_, value); + } + + // Resizes the array to |size| and makes it non-null. Otherwise, works just + // like the resize method of |std::vector|. + void resize(size_t size) { + is_null_ = false; + Traits::Resize(&vec_, size); + } + + // Returns a const reference to the |std::vector| managed by this class. If + // the array is null, this will be an empty vector. + const std::vector<StorageType>& storage() const { return vec_; } + operator const std::vector<StorageType>&() const { return vec_; } + + // Swaps the contents of this array with the |other| array, including + // nullness. + void Swap(Array* other) { + std::swap(is_null_, other->is_null_); + vec_.swap(other->vec_); + } + + // Swaps the contents of this array with the specified vector, making this + // array non-null. Since the vector cannot represent null, it will just be + // made empty if this array is null. + void Swap(std::vector<StorageType>* other) { + is_null_ = false; + vec_.swap(*other); + } + + // Returns a copy of the array where each value of the new array has been + // "cloned" from the corresponding value of this array. If this array contains + // primitive data types, this is equivalent to simply copying the contents. + // However, if the array contains objects, then each new element is created by + // calling the |Clone| method of the source element, which should make a copy + // of the element. + // + // Please note that calling this method will fail compilation if the element + // type cannot be cloned (which usually means that it is a Mojo handle type or + // a type contains Mojo handles). + Array Clone() const { + Array result; + result.is_null_ = is_null_; + Traits::Clone(vec_, &result.vec_); + return result.Pass(); + } + + // Indicates whether the contents of this array are equal to |other|. A null + // array is only equal to another null array. Elements are compared using the + // |ValueTraits::Equals| method, which in most cases calls the |Equals| method + // of the element. + bool Equals(const Array& other) const { + if (is_null() != other.is_null()) + return false; + if (size() != other.size()) + return false; + for (size_t i = 0; i < size(); ++i) { + if (!internal::ValueTraits<T>::Equals(at(i), other.at(i))) + return false; + } + return true; + } + + private: + typedef std::vector<StorageType> Array::*Testable; + + public: + operator Testable() const { return is_null_ ? 0 : &Array::vec_; } + + private: + // Forbid the == and != operators explicitly, otherwise Array will be + // converted to Testable to do == or != comparison. + template <typename U> + bool operator==(const Array<U>& other) const = delete; + template <typename U> + bool operator!=(const Array<U>& other) const = delete; + + void Take(Array* other) { + reset(); + Swap(other); + } + + std::vector<StorageType> vec_; + bool is_null_; +}; + +// A |TypeConverter| that will create an |Array<T>| containing a copy of the +// contents of an |std::vector<E>|, using |TypeConverter<T, E>| to copy each +// element. The returned array will always be non-null. +template <typename T, typename E> +struct TypeConverter<Array<T>, std::vector<E>> { + static Array<T> Convert(const std::vector<E>& input) { + Array<T> result(input.size()); + for (size_t i = 0; i < input.size(); ++i) + result[i] = TypeConverter<T, E>::Convert(input[i]); + return result.Pass(); + } +}; + +// A |TypeConverter| that will create an |std::vector<E>| containing a copy of +// the contents of an |Array<T>|, using |TypeConverter<E, T>| to copy each +// element. If the input array is null, the output vector will be empty. +template <typename E, typename T> +struct TypeConverter<std::vector<E>, Array<T>> { + static std::vector<E> Convert(const Array<T>& input) { + std::vector<E> result; + if (!input.is_null()) { + result.resize(input.size()); + for (size_t i = 0; i < input.size(); ++i) + result[i] = TypeConverter<E, T>::Convert(input[i]); + } + return result; + } +}; + +// A |TypeConverter| that will create an |Array<T>| containing a copy of the +// contents of an |std::set<E>|, using |TypeConverter<T, E>| to copy each +// element. The returned array will always be non-null. +template <typename T, typename E> +struct TypeConverter<Array<T>, std::set<E>> { + static Array<T> Convert(const std::set<E>& input) { + Array<T> result(0u); + for (auto i : input) + result.push_back(TypeConverter<T, E>::Convert(i)); + return result.Pass(); + } +}; + +// A |TypeConverter| that will create an |std::set<E>| containing a copy of +// the contents of an |Array<T>|, using |TypeConverter<E, T>| to copy each +// element. If the input array is null, the output set will be empty. +template <typename E, typename T> +struct TypeConverter<std::set<E>, Array<T>> { + static std::set<E> Convert(const Array<T>& input) { + std::set<E> result; + if (!input.is_null()) { + for (size_t i = 0; i < input.size(); ++i) + result.insert(TypeConverter<E, T>::Convert(input[i])); + } + return result; + } +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_ARRAY_H_ diff --git a/mojo/public/cpp/bindings/associated_interface_ptr_info.h b/mojo/public/cpp/bindings/associated_interface_ptr_info.h new file mode 100644 index 0000000..55f9c4a --- /dev/null +++ b/mojo/public/cpp/bindings/associated_interface_ptr_info.h @@ -0,0 +1,32 @@ +// Copyright 2015 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_ASSOCIATED_INTERFACE_PTR_INFO_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_PTR_INFO_H_ + +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { + +// AssociatedInterfacePtrInfo stores necessary information to construct an +// associated interface pointer. +// TODO(yzshen): implement it. +template <typename Interface> +class AssociatedInterfacePtrInfo { + MOJO_MOVE_ONLY_TYPE(AssociatedInterfacePtrInfo); + + public: + AssociatedInterfacePtrInfo() {} + AssociatedInterfacePtrInfo(AssociatedInterfacePtrInfo&& other) {} + + AssociatedInterfacePtrInfo& operator=(AssociatedInterfacePtrInfo&& other) { + return *this; + } + + bool is_valid() const { return false; } +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_PTR_INFO_H_ diff --git a/mojo/public/cpp/bindings/associated_interface_request.h b/mojo/public/cpp/bindings/associated_interface_request.h new file mode 100644 index 0000000..9882b89 --- /dev/null +++ b/mojo/public/cpp/bindings/associated_interface_request.h @@ -0,0 +1,31 @@ +// Copyright 2015 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_ASSOCIATED_INTERFACE_REQUEST_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_REQUEST_H_ + +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { + +// Represents an associated interface request. +// TODO(yzshen): implement it. +template <typename Interface> +class AssociatedInterfaceRequest { + MOJO_MOVE_ONLY_TYPE(AssociatedInterfaceRequest) + + public: + AssociatedInterfaceRequest() {} + AssociatedInterfaceRequest(AssociatedInterfaceRequest&& other) {} + + AssociatedInterfaceRequest& operator=(AssociatedInterfaceRequest&& other) { + return *this; + } + + bool is_pending() const { return false; } +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_REQUEST_H_ diff --git a/mojo/public/cpp/bindings/binding.h b/mojo/public/cpp/bindings/binding.h new file mode 100644 index 0000000..de9159f --- /dev/null +++ b/mojo/public/cpp/bindings/binding.h @@ -0,0 +1,239 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_BINDING_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_BINDING_H_ + +#include "mojo/public/c/environment/async_waiter.h" +#include "mojo/public/cpp/bindings/callback.h" +#include "mojo/public/cpp/bindings/interface_ptr.h" +#include "mojo/public/cpp/bindings/interface_ptr_info.h" +#include "mojo/public/cpp/bindings/interface_request.h" +#include "mojo/public/cpp/bindings/lib/filter_chain.h" +#include "mojo/public/cpp/bindings/lib/message_header_validator.h" +#include "mojo/public/cpp/bindings/lib/router.h" +#include "mojo/public/cpp/environment/logging.h" +#include "mojo/public/cpp/system/core.h" + +namespace mojo { + +// Represents the binding of an interface implementation to a message pipe. +// When the |Binding| object is destroyed, the binding between the message pipe +// and the interface is torn down and the message pipe is closed, leaving the +// interface implementation in an unbound state. +// +// Example: +// +// #include "foo.mojom.h" +// +// class FooImpl : public Foo { +// public: +// explicit FooImpl(InterfaceRequest<Foo> request) +// : binding_(this, request.Pass()) {} +// +// // Foo implementation here. +// +// private: +// Binding<Foo> binding_; +// }; +// +// class MyFooFactory : public InterfaceFactory<Foo> { +// public: +// void Create(..., InterfaceRequest<Foo> request) override { +// auto f = new FooImpl(request.Pass()); +// // Do something to manage the lifetime of |f|. Use StrongBinding<> to +// // delete FooImpl on connection errors. +// } +// }; +// +// The caller may specify a |MojoAsyncWaiter| to be used by the connection when +// waiting for calls to arrive. Normally it is fine to use the default waiter. +// However, the caller may provide their own implementation if needed. The +// |Binding| will not take ownership of the waiter, and the waiter must outlive +// the |Binding|. The provided waiter must be able to signal the implementation +// which generally means it needs to be able to schedule work on the thread the +// implementation runs on. If writing library code that has to work on different +// types of threads callers may need to provide different waiter +// implementations. +template <typename Interface> +class Binding { + public: + // 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) : impl_(impl) { stub_.set_sink(impl_); } + + // Constructs a completed binding of message pipe |handle| to implementation + // |impl|. Does not take ownership of |impl|, which must outlive the binding. + // See class comment for definition of |waiter|. + Binding(Interface* impl, + ScopedMessagePipeHandle handle, + const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) + : Binding(impl) { + Bind(handle.Pass(), waiter); + } + + // Constructs a completed binding of |impl| to a new message pipe, passing the + // client end to |ptr|, which takes ownership of it. The caller is expected to + // 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. See class comment for definition of + // |waiter|. + Binding(Interface* impl, + InterfacePtr<Interface>* ptr, + const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) + : Binding(impl) { + Bind(ptr, waiter); + } + + // 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. See class comment for definition of + // |waiter|. + Binding(Interface* impl, + InterfaceRequest<Interface> request, + const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) + : Binding(impl) { + Bind(request.PassMessagePipe(), waiter); + } + + // Tears down the binding, closing the message pipe and leaving the interface + // implementation unbound. + ~Binding() { + if (internal_router_) + Close(); + } + + // Completes a binding that was constructed with only an interface + // implementation. Takes ownership of |handle| and binds it to the previously + // specified implementation. See class comment for definition of |waiter|. + void Bind( + ScopedMessagePipeHandle handle, + const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) { + MOJO_DCHECK(!internal_router_); + internal::FilterChain filters; + filters.Append<internal::MessageHeaderValidator>(); + filters.Append<typename Interface::RequestValidator_>(); + + internal_router_ = + new internal::Router(handle.Pass(), filters.Pass(), waiter); + internal_router_->set_incoming_receiver(&stub_); + internal_router_->set_connection_error_handler( + [this]() { connection_error_handler_.Run(); }); + } + + // Completes a binding that was constructed with only an interface + // implementation by creating a new message pipe, binding one end of it to the + // previously specified implementation, and passing the other to |ptr|, which + // takes ownership of it. The caller is expected to pass |ptr| on to the + // eventual client of the service. Does not take ownership of |ptr|. See + // class comment for definition of |waiter|. + void Bind( + InterfacePtr<Interface>* ptr, + const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) { + MessagePipe pipe; + ptr->Bind( + InterfacePtrInfo<Interface>(pipe.handle0.Pass(), Interface::Version_), + waiter); + Bind(pipe.handle1.Pass(), waiter); + } + + // Completes a binding that was constructed with only an interface + // implementation by removing the message pipe endpoint from |request| and + // binding it to the previously specified implementation. See class comment + // for definition of |waiter|. + void Bind( + InterfaceRequest<Interface> request, + const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) { + Bind(request.PassMessagePipe(), waiter); + } + + // Stops processing incoming messages until + // ResumeIncomingMethodCallProcessing(), or WaitForIncomingMethodCall(). + // Outgoing messages are still sent. + // + // No errors are detected on the message pipe while paused. + void PauseIncomingMethodCallProcessing() { + MOJO_DCHECK(internal_router_); + internal_router_->PauseIncomingMethodCallProcessing(); + } + void ResumeIncomingMethodCallProcessing() { + MOJO_DCHECK(internal_router_); + internal_router_->ResumeIncomingMethodCallProcessing(); + } + + // Blocks the calling thread until either a call arrives on the previously + // bound message pipe, the deadline is exceeded, or an error occurs. Returns + // true if a method was successfully read and dispatched. + bool WaitForIncomingMethodCall( + MojoDeadline deadline = MOJO_DEADLINE_INDEFINITE) { + MOJO_DCHECK(internal_router_); + return internal_router_->WaitForIncomingMessage(deadline); + } + + // Closes the message pipe that was previously bound. Put this object into a + // state where it can be rebound to a new pipe. + void Close() { + MOJO_DCHECK(internal_router_); + internal_router_->CloseMessagePipe(); + DestroyRouter(); + } + + // Unbinds the underlying pipe from this binding and returns it so it can be + // used in another context, such as on another thread or with a different + // implementation. Put this object into a state where it can be rebound to a + // new pipe. + InterfaceRequest<Interface> Unbind() { + InterfaceRequest<Interface> request = + MakeRequest<Interface>(internal_router_->PassMessagePipe()); + DestroyRouter(); + // TODO(vtl): The |.Pass()| below is only needed due to an MSVS bug; remove + // it once that's fixed. + return request.Pass(); + } + + // Sets an error handler that will be called if a connection error occurs on + // the bound message pipe. + void set_connection_error_handler(const Closure& error_handler) { + connection_error_handler_ = error_handler; + } + + // Returns the interface implementation that was previously specified. Caller + // does not take ownership. + Interface* impl() { return impl_; } + + // Indicates whether the binding has been completed (i.e., whether a message + // pipe has been bound to the implementation). + bool is_bound() const { return !!internal_router_; } + + // Returns the value of the handle currently bound to this Binding which can + // be used to make explicit Wait/WaitMany calls. Requires that the Binding be + // bound. Ownership of the handle is retained by the Binding, it is not + // transferred to the caller. + MessagePipeHandle handle() const { + MOJO_DCHECK(is_bound()); + return internal_router_->handle(); + } + + // Exposed for testing, should not generally be used. + internal::Router* internal_router() { return internal_router_; } + + private: + void DestroyRouter() { + internal_router_->set_connection_error_handler(Closure()); + delete internal_router_; + internal_router_ = nullptr; + } + + internal::Router* internal_router_ = nullptr; + typename Interface::Stub_ stub_; + Interface* impl_; + Closure connection_error_handler_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(Binding); +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_BINDING_H_ diff --git a/mojo/public/cpp/bindings/callback.h b/mojo/public/cpp/bindings/callback.h new file mode 100644 index 0000000..beec1a1 --- /dev/null +++ b/mojo/public/cpp/bindings/callback.h @@ -0,0 +1,115 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_CALLBACK_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_CALLBACK_H_ + +#include "mojo/public/cpp/bindings/lib/callback_internal.h" +#include "mojo/public/cpp/bindings/lib/shared_ptr.h" +#include "mojo/public/cpp/bindings/lib/template_util.h" + +namespace mojo { + +template <typename Sig> +class Callback; + +// Represents a callback with any number of parameters and no return value. The +// callback is executed by calling its Run() method. The callback may be "null", +// meaning it does nothing. +template <typename... Args> +class Callback<void(Args...)> { + public: + // An interface that may be implemented to define the Run() method. + struct Runnable { + virtual ~Runnable() {} + virtual void Run( + // ForwardType ensures String is passed as a const reference. + typename internal::Callback_ParamTraits<Args>::ForwardType...) + const = 0; + }; + + // Constructs a "null" callback that does nothing. + Callback() {} + + // Constructs a callback that will run |runnable|. The callback takes + // ownership of |runnable|. + explicit Callback(Runnable* runnable) : sink_(runnable) {} + + // As above, but can take an object that isn't derived from Runnable, so long + // as it has a compatible operator() or Run() method. operator() will be + // preferred if the type has both. + template <typename Sink> + Callback(const Sink& sink) { + using sink_type = typename internal::Conditional< + internal::HasCompatibleCallOperator<Sink, Args...>::value, + FunctorAdapter<Sink>, RunnableAdapter<Sink>>::type; + sink_ = internal::SharedPtr<Runnable>(new sink_type(sink)); + } + + // As above, but can take a compatible function pointer. + Callback(void (*function_ptr)( + typename internal::Callback_ParamTraits<Args>::ForwardType...)) + : sink_(new FunctionPtrAdapter(function_ptr)) {} + + // Executes the callback function, invoking Pass() on move-only types. + void Run(typename internal::Callback_ParamTraits<Args>::ForwardType... args) + const { + if (sink_.get()) + sink_->Run(internal::Forward(args)...); + } + + bool is_null() const { return !sink_.get(); } + + // Resets the callback to the "null" state. + void reset() { sink_.reset(); } + + private: + // Adapts a class that has a Run() method but is not derived from Runnable to + // be callable by Callback. + template <typename Sink> + struct RunnableAdapter : public Runnable { + explicit RunnableAdapter(const Sink& sink) : sink(sink) {} + virtual void Run( + typename internal::Callback_ParamTraits<Args>::ForwardType... args) + const override { + sink.Run(internal::Forward(args)...); + } + Sink sink; + }; + + // Adapts a class that has a compatible operator() to be callable by Callback. + template <typename Sink> + struct FunctorAdapter : public Runnable { + explicit FunctorAdapter(const Sink& sink) : sink(sink) {} + virtual void Run( + typename internal::Callback_ParamTraits<Args>::ForwardType... args) + const override { + sink.operator()(internal::Forward(args)...); + } + Sink sink; + }; + + // Adapts a function pointer. + struct FunctionPtrAdapter : public Runnable { + explicit FunctionPtrAdapter(void (*function_ptr)( + typename internal::Callback_ParamTraits<Args>::ForwardType...)) + : function_ptr(function_ptr) {} + virtual void Run( + typename internal::Callback_ParamTraits<Args>::ForwardType... args) + const override { + (*function_ptr)(internal::Forward(args)...); + } + void (*function_ptr)( + typename internal::Callback_ParamTraits<Args>::ForwardType...); + }; + + internal::SharedPtr<Runnable> sink_; +}; + +// A specialization of Callback which takes no parameters. +typedef Callback<void()> Closure; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_CALLBACK_H_ diff --git a/mojo/public/cpp/bindings/interface_ptr.h b/mojo/public/cpp/bindings/interface_ptr.h new file mode 100644 index 0000000..dc44667 --- /dev/null +++ b/mojo/public/cpp/bindings/interface_ptr.h @@ -0,0 +1,197 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_H_ + +#include <algorithm> + +#include "mojo/public/cpp/bindings/callback.h" +#include "mojo/public/cpp/bindings/interface_ptr_info.h" +#include "mojo/public/cpp/bindings/lib/interface_ptr_internal.h" +#include "mojo/public/cpp/environment/environment.h" +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { + +// A pointer to a local proxy of a remote Interface implementation. Uses a +// message pipe to communicate with the remote implementation, and automatically +// closes the pipe and deletes the proxy on destruction. The pointer must be +// bound to a message pipe before the interface methods can be called. +// +// This class is thread hostile, as is the local proxy it manages. All calls to +// this class or the proxy should be from the same thread that created it. If +// you need to move the proxy to a different thread, extract the +// InterfacePtrInfo (containing just the message pipe and any version +// information) using PassInterface(), pass it to a different thread, and +// create and bind a new InterfacePtr from that thread. +template <typename Interface> +class InterfacePtr { + MOJO_MOVE_ONLY_TYPE(InterfacePtr) + public: + // Constructs an unbound InterfacePtr. + InterfacePtr() {} + InterfacePtr(decltype(nullptr)) {} + + // Takes over the binding of another InterfacePtr. + InterfacePtr(InterfacePtr&& other) { + internal_state_.Swap(&other.internal_state_); + } + + // Takes over the binding of another InterfacePtr, and closes any message pipe + // already bound to this pointer. + InterfacePtr& operator=(InterfacePtr&& other) { + reset(); + internal_state_.Swap(&other.internal_state_); + return *this; + } + + // Assigning nullptr to this class causes it to close the currently bound + // message pipe (if any) and returns the pointer to the unbound state. + InterfacePtr& operator=(decltype(nullptr)) { + reset(); + return *this; + } + + // Closes the bound message pipe (if any) on destruction. + ~InterfacePtr() {} + + // Binds the InterfacePtr to a remote implementation of Interface. The + // |waiter| is used for receiving notifications when there is data to read + // from the message pipe. For most callers, the default |waiter| will be + // sufficient. + // + // Calling with an invalid |info| (containing an invalid message pipe handle) + // has the same effect as reset(). In this case, the InterfacePtr is not + // considered as bound. + void Bind( + InterfacePtrInfo<Interface> info, + const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) { + reset(); + if (info.is_valid()) + internal_state_.Bind(info.Pass(), waiter); + } + + // Returns whether or not this InterfacePtr is bound to a message pipe. + bool is_bound() const { return internal_state_.is_bound(); } + + // Returns a raw pointer to the local proxy. Caller does not take ownership. + // Note that the local proxy is thread hostile, as stated above. + Interface* get() const { return internal_state_.instance(); } + + // Functions like a pointer to Interface. Must already be bound. + Interface* operator->() const { return get(); } + Interface& operator*() const { return *get(); } + + // Returns the version number of the interface that the remote side supports. + uint32_t version() const { return internal_state_.version(); } + + // Queries the max version that the remote side supports. On completion, the + // result will be returned as the input of |callback|. The version number of + // this interface pointer will also be updated. + void QueryVersion(const Callback<void(uint32_t)>& callback) { + internal_state_.QueryVersion(callback); + } + + // If the remote side doesn't support the specified version, it will close its + // end of the message pipe asynchronously. This does nothing if it's already + // known that the remote side supports the specified version, i.e., if + // |version <= this->version()|. + // + // After calling RequireVersion() with a version not supported by the remote + // side, all subsequent calls to interface methods will be ignored. + void RequireVersion(uint32_t version) { + internal_state_.RequireVersion(version); + } + + // Closes the bound message pipe (if any) and returns the pointer to the + // unbound state. + void reset() { + State doomed; + internal_state_.Swap(&doomed); + } + + // Blocks the current thread until the next incoming response callback arrives + // or an error occurs. Returns |true| if a response arrived, or |false| in + // case of error. + // + // This method may only be called after the InterfacePtr has been bound to a + // message pipe. + bool WaitForIncomingResponse() { + return internal_state_.WaitForIncomingResponse(); + } + + // Indicates whether the message pipe has encountered an error. If true, + // method calls made on this interface will be dropped (and may already have + // been dropped). + bool encountered_error() const { return internal_state_.encountered_error(); } + + // Registers a handler to receive error notifications. The handler will be + // called from the thread that owns this InterfacePtr. + // + // This method may only be called after the InterfacePtr has been bound to a + // message pipe. + void set_connection_error_handler(const Closure& error_handler) { + internal_state_.set_connection_error_handler(error_handler); + } + + // Unbinds the InterfacePtr and returns the information which could be used + // to setup an InterfacePtr again. This method may be used to move the proxy + // to a different thread (see class comments for details). + // + // It is an error to call PassInterface() while there are pending responses. + // TODO: fix this restriction, it's not always obvious when there is a + // pending response. + InterfacePtrInfo<Interface> PassInterface() { + MOJO_DCHECK(!internal_state_.has_pending_callbacks()); + State state; + internal_state_.Swap(&state); + + return state.PassInterface(); + } + + // DO NOT USE. Exposed only for internal use and for testing. + internal::InterfacePtrState<Interface>* internal_state() { + return &internal_state_; + } + + // Allow InterfacePtr<> to be used in boolean expressions, but not + // implicitly convertible to a real bool (which is dangerous). + private: + typedef internal::InterfacePtrState<Interface> InterfacePtr::*Testable; + + public: + operator Testable() const { + return internal_state_.is_bound() ? &InterfacePtr::internal_state_ + : nullptr; + } + + private: + // Forbid the == and != operators explicitly, otherwise InterfacePtr will be + // converted to Testable to do == or != comparison. + template <typename T> + bool operator==(const InterfacePtr<T>& other) const = delete; + template <typename T> + bool operator!=(const InterfacePtr<T>& other) const = delete; + + typedef internal::InterfacePtrState<Interface> State; + mutable State internal_state_; +}; + +// If |info| is valid (containing a valid message pipe handle), returns an +// InterfacePtr bound to it. Otherwise, returns an unbound InterfacePtr. The +// specified |waiter| will be used as in the InterfacePtr::Bind() method. +template <typename Interface> +InterfacePtr<Interface> MakeProxy( + InterfacePtrInfo<Interface> info, + const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) { + InterfacePtr<Interface> ptr; + if (info.is_valid()) + ptr.Bind(info.Pass(), waiter); + return ptr.Pass(); +} + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_H_ diff --git a/mojo/public/cpp/bindings/interface_ptr_info.h b/mojo/public/cpp/bindings/interface_ptr_info.h new file mode 100644 index 0000000..4f61915 --- /dev/null +++ b/mojo/public/cpp/bindings/interface_ptr_info.h @@ -0,0 +1,58 @@ +// Copyright 2015 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_INTERFACE_PTR_INFO_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_INFO_H_ + +#include "mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/system/message_pipe.h" + +namespace mojo { + +// InterfacePtrInfo stores necessary information to communicate with a remote +// interface implementation, which could be used to construct an InterfacePtr. +template <typename Interface> +class InterfacePtrInfo { + MOJO_MOVE_ONLY_TYPE(InterfacePtrInfo); + + public: + InterfacePtrInfo() : version_(0u) {} + + InterfacePtrInfo(ScopedMessagePipeHandle handle, uint32_t version) + : handle_(handle.Pass()), version_(version) {} + + InterfacePtrInfo(InterfacePtrInfo&& other) + : handle_(other.handle_.Pass()), version_(other.version_) { + other.version_ = 0u; + } + + ~InterfacePtrInfo() {} + + InterfacePtrInfo& operator=(InterfacePtrInfo&& other) { + if (this != &other) { + handle_ = other.handle_.Pass(); + version_ = other.version_; + other.version_ = 0u; + } + + return *this; + } + + bool is_valid() const { return handle_.is_valid(); } + + ScopedMessagePipeHandle PassHandle() { return handle_.Pass(); } + const ScopedMessagePipeHandle& handle() const { return handle_; } + void set_handle(ScopedMessagePipeHandle handle) { handle_ = handle.Pass(); } + + uint32_t version() const { return version_; } + void set_version(uint32_t version) { version_ = version; } + + private: + ScopedMessagePipeHandle handle_; + uint32_t version_; +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_INFO_H_ diff --git a/mojo/public/cpp/bindings/interface_request.h b/mojo/public/cpp/bindings/interface_request.h new file mode 100644 index 0000000..c139306 --- /dev/null +++ b/mojo/public/cpp/bindings/interface_request.h @@ -0,0 +1,118 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_REQUEST_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_REQUEST_H_ + +#include "mojo/public/cpp/bindings/interface_ptr.h" + +namespace mojo { + +// Represents a request from a remote client for an implementation of Interface +// over a specified message pipe. The implementor of the interface should +// remove the message pipe by calling PassMessagePipe() and bind it to the +// implementation. If this is not done, the InterfaceRequest will automatically +// close the pipe on destruction. Can also represent the absence of a request +// if the client did not provide a message pipe. +template <typename Interface> +class InterfaceRequest { + MOJO_MOVE_ONLY_TYPE(InterfaceRequest) + public: + // Constructs an empty InterfaceRequest, representing that the client is not + // requesting an implementation of Interface. + InterfaceRequest() {} + InterfaceRequest(decltype(nullptr)) {} + + // Takes the message pipe from another InterfaceRequest. + InterfaceRequest(InterfaceRequest&& other) { handle_ = other.handle_.Pass(); } + InterfaceRequest& operator=(InterfaceRequest&& other) { + handle_ = other.handle_.Pass(); + return *this; + } + + // Assigning to nullptr resets the InterfaceRequest to an empty state, + // closing the message pipe currently bound to it (if any). + InterfaceRequest& operator=(decltype(nullptr)) { + handle_.reset(); + return *this; + } + + // Binds the request to a message pipe over which Interface is to be + // requested. If the request is already bound to a message pipe, the current + // message pipe will be closed. + void Bind(ScopedMessagePipeHandle handle) { handle_ = handle.Pass(); } + + // Indicates whether the request currently contains a valid message pipe. + bool is_pending() const { return handle_.is_valid(); } + + // Removes the message pipe from the request and returns it. + ScopedMessagePipeHandle PassMessagePipe() { return handle_.Pass(); } + + private: + ScopedMessagePipeHandle handle_; +}; + +// Makes an InterfaceRequest bound to the specified message pipe. If |handle| +// is empty or invalid, the resulting InterfaceRequest will represent the +// absence of a request. +template <typename Interface> +InterfaceRequest<Interface> MakeRequest(ScopedMessagePipeHandle handle) { + InterfaceRequest<Interface> request; + request.Bind(handle.Pass()); + return request.Pass(); +} + +// Creates a new message pipe over which Interface is to be served. Binds the +// specified InterfacePtr to one end of the message pipe, and returns an +// InterfaceRequest bound to the other. The InterfacePtr should be passed to +// the client, and the InterfaceRequest should be passed to whatever will +// provide the implementation. The implementation should typically be bound to +// the InterfaceRequest using the Binding or StrongBinding classes. The client +// may begin to issue calls even before an implementation has been bound, since +// messages sent over the pipe will just queue up until they are consumed by +// the implementation. +// +// Example #1: Requesting a remote implementation of an interface. +// =============================================================== +// +// Given the following interface: +// +// interface Database { +// OpenTable(Table& table); +// } +// +// The client would have code similar to the following: +// +// DatabasePtr database = ...; // Connect to database. +// TablePtr table; +// database->OpenTable(GetProxy(&table)); +// +// Upon return from GetProxy, |table| is ready to have methods called on it. +// +// Example #2: Registering a local implementation with a remote service. +// ===================================================================== +// +// Given the following interface +// interface Collector { +// RegisterSource(Source source); +// } +// +// The client would have code similar to the following: +// +// CollectorPtr collector = ...; // Connect to Collector. +// SourcePtr source; +// InterfaceRequest<Source> source_request = GetProxy(&source); +// collector->RegisterSource(source.Pass()); +// CreateSource(source_request.Pass()); // Create implementation locally. +// +template <typename Interface> +InterfaceRequest<Interface> GetProxy(InterfacePtr<Interface>* ptr) { + MessagePipe pipe; + ptr->Bind(InterfacePtrInfo<Interface>(pipe.handle0.Pass(), 0u)); + return MakeRequest<Interface>(pipe.handle1.Pass()); +} + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_REQUEST_H_ diff --git a/mojo/public/cpp/bindings/lib/TODO b/mojo/public/cpp/bindings/lib/TODO new file mode 100644 index 0000000..ea4ce81 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/TODO @@ -0,0 +1,4 @@ +TODOs: + - Optimize Buffer classes? + - Add compile-time asserts to verify object packing and padding. + - Investigate making arrays of objects not be arrays of pointers. diff --git a/mojo/public/cpp/bindings/lib/array_internal.cc b/mojo/public/cpp/bindings/lib/array_internal.cc new file mode 100644 index 0000000..61e4b0d --- /dev/null +++ b/mojo/public/cpp/bindings/lib/array_internal.cc @@ -0,0 +1,74 @@ +// 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. + +#include "mojo/public/cpp/bindings/lib/array_internal.h" + +#include <sstream> + +namespace mojo { +namespace internal { + +std::string MakeMessageWithArrayIndex(const char* message, + size_t size, + size_t index) { + std::ostringstream stream; + stream << message << ": array size - " << size << "; index - " << index; + return stream.str(); +} + +std::string MakeMessageWithExpectedArraySize(const char* message, + size_t size, + size_t expected_size) { + std::ostringstream stream; + stream << message << ": array size - " << size << "; expected size - " + << expected_size; + return stream.str(); +} + +ArrayDataTraits<bool>::BitRef::~BitRef() { +} + +ArrayDataTraits<bool>::BitRef::BitRef(uint8_t* storage, uint8_t mask) + : storage_(storage), mask_(mask) { +} + +ArrayDataTraits<bool>::BitRef& ArrayDataTraits<bool>::BitRef::operator=( + bool value) { + if (value) { + *storage_ |= mask_; + } else { + *storage_ &= ~mask_; + } + return *this; +} + +ArrayDataTraits<bool>::BitRef& ArrayDataTraits<bool>::BitRef::operator=( + const BitRef& value) { + return (*this) = static_cast<bool>(value); +} + +ArrayDataTraits<bool>::BitRef::operator bool() const { + return (*storage_ & mask_) != 0; +} + +// static +void ArraySerializationHelper<Handle, true>::EncodePointersAndHandles( + const ArrayHeader* header, + ElementType* elements, + std::vector<Handle>* handles) { + for (uint32_t i = 0; i < header->num_elements; ++i) + EncodeHandle(&elements[i], handles); +} + +// static +void ArraySerializationHelper<Handle, true>::DecodePointersAndHandles( + const ArrayHeader* header, + ElementType* elements, + std::vector<Handle>* handles) { + for (uint32_t i = 0; i < header->num_elements; ++i) + DecodeHandle(&elements[i], handles); +} + +} // namespace internal +} // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/array_internal.h b/mojo/public/cpp/bindings/lib/array_internal.h new file mode 100644 index 0000000..41ca174 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/array_internal.h @@ -0,0 +1,534 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_INTERNAL_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_INTERNAL_H_ + +#include <new> +#include <vector> + +#include "mojo/public/c/system/macros.h" +#include "mojo/public/cpp/bindings/lib/bindings_internal.h" +#include "mojo/public/cpp/bindings/lib/bindings_serialization.h" +#include "mojo/public/cpp/bindings/lib/bounds_checker.h" +#include "mojo/public/cpp/bindings/lib/buffer.h" +#include "mojo/public/cpp/bindings/lib/map_data_internal.h" +#include "mojo/public/cpp/bindings/lib/template_util.h" +#include "mojo/public/cpp/bindings/lib/validate_params.h" +#include "mojo/public/cpp/bindings/lib/validation_errors.h" +#include "mojo/public/cpp/environment/logging.h" + +namespace mojo { +template <typename T> +class Array; +class String; + +namespace internal { + +// std::numeric_limits<uint32_t>::max() is not a compile-time constant (until +// C++11). +const uint32_t kMaxUint32 = 0xFFFFFFFF; + +std::string MakeMessageWithArrayIndex(const char* message, + size_t size, + size_t index); + +std::string MakeMessageWithExpectedArraySize(const char* message, + size_t size, + size_t expected_size); + +template <typename T> +struct ArrayDataTraits { + typedef T StorageType; + typedef T& Ref; + typedef T const& ConstRef; + + static const uint32_t kMaxNumElements = + (kMaxUint32 - sizeof(ArrayHeader)) / sizeof(StorageType); + + static uint32_t GetStorageSize(uint32_t num_elements) { + MOJO_DCHECK(num_elements <= kMaxNumElements); + return sizeof(ArrayHeader) + sizeof(StorageType) * num_elements; + } + static Ref ToRef(StorageType* storage, size_t offset) { + return storage[offset]; + } + static ConstRef ToConstRef(const StorageType* storage, size_t offset) { + return storage[offset]; + } +}; + +template <typename P> +struct ArrayDataTraits<P*> { + typedef StructPointer<P> StorageType; + typedef P*& Ref; + typedef P* const& ConstRef; + + static const uint32_t kMaxNumElements = + (kMaxUint32 - sizeof(ArrayHeader)) / sizeof(StorageType); + + static uint32_t GetStorageSize(uint32_t num_elements) { + MOJO_DCHECK(num_elements <= kMaxNumElements); + return sizeof(ArrayHeader) + sizeof(StorageType) * num_elements; + } + static Ref ToRef(StorageType* storage, size_t offset) { + return storage[offset].ptr; + } + static ConstRef ToConstRef(const StorageType* storage, size_t offset) { + return storage[offset].ptr; + } +}; + +template <typename T> +struct ArrayDataTraits<Array_Data<T>*> { + typedef ArrayPointer<T> StorageType; + typedef Array_Data<T>*& Ref; + typedef Array_Data<T>* const& ConstRef; + + static const uint32_t kMaxNumElements = + (kMaxUint32 - sizeof(ArrayHeader)) / sizeof(StorageType); + + static uint32_t GetStorageSize(uint32_t num_elements) { + MOJO_DCHECK(num_elements <= kMaxNumElements); + return sizeof(ArrayHeader) + sizeof(StorageType) * num_elements; + } + static Ref ToRef(StorageType* storage, size_t offset) { + return storage[offset].ptr; + } + static ConstRef ToConstRef(const StorageType* storage, size_t offset) { + return storage[offset].ptr; + } +}; + +// Specialization of Arrays for bools, optimized for space. It has the +// following differences from a generalized Array: +// * Each element takes up a single bit of memory. +// * Accessing a non-const single element uses a helper class |BitRef|, which +// emulates a reference to a bool. +template <> +struct ArrayDataTraits<bool> { + // Helper class to emulate a reference to a bool, used for direct element + // access. + class BitRef { + public: + ~BitRef(); + BitRef& operator=(bool value); + BitRef& operator=(const BitRef& value); + operator bool() const; + + private: + friend struct ArrayDataTraits<bool>; + BitRef(uint8_t* storage, uint8_t mask); + BitRef(); + uint8_t* storage_; + uint8_t mask_; + }; + + // Because each element consumes only 1/8 byte. + static const uint32_t kMaxNumElements = kMaxUint32; + + typedef uint8_t StorageType; + typedef BitRef Ref; + typedef bool ConstRef; + + static uint32_t GetStorageSize(uint32_t num_elements) { + return sizeof(ArrayHeader) + ((num_elements + 7) / 8); + } + static BitRef ToRef(StorageType* storage, size_t offset) { + return BitRef(&storage[offset / 8], 1 << (offset % 8)); + } + static bool ToConstRef(const StorageType* storage, size_t offset) { + return (storage[offset / 8] & (1 << (offset % 8))) != 0; + } +}; + +// What follows is code to support the serialization of Array_Data<T>. There +// are two interesting cases: arrays of primitives and arrays of objects. +// Arrays of objects are represented as arrays of pointers to objects. + +template <typename T, bool is_handle> +struct ArraySerializationHelper; + +template <typename T> +struct ArraySerializationHelper<T, false> { + typedef typename ArrayDataTraits<T>::StorageType ElementType; + + static void EncodePointersAndHandles(const ArrayHeader* header, + ElementType* elements, + std::vector<Handle>* handles) {} + + static void DecodePointersAndHandles(const ArrayHeader* header, + ElementType* elements, + std::vector<Handle>* handles) {} + + static bool ValidateElements(const ArrayHeader* header, + const ElementType* elements, + BoundsChecker* bounds_checker, + const ArrayValidateParams* validate_params) { + MOJO_DCHECK(!validate_params->element_is_nullable) + << "Primitive type should be non-nullable"; + MOJO_DCHECK(!validate_params->element_validate_params) + << "Primitive type should not have array validate params"; + return true; + } +}; + +template <> +struct ArraySerializationHelper<Handle, true> { + typedef ArrayDataTraits<Handle>::StorageType ElementType; + + static void EncodePointersAndHandles(const ArrayHeader* header, + ElementType* elements, + std::vector<Handle>* handles); + + static void DecodePointersAndHandles(const ArrayHeader* header, + ElementType* elements, + std::vector<Handle>* handles); + + static bool ValidateElements(const ArrayHeader* header, + const ElementType* elements, + BoundsChecker* bounds_checker, + const ArrayValidateParams* validate_params) { + MOJO_DCHECK(!validate_params->element_validate_params) + << "Handle type should not have array validate params"; + + for (uint32_t i = 0; i < header->num_elements; ++i) { + if (!validate_params->element_is_nullable && + elements[i].value() == kEncodedInvalidHandleValue) { + ReportValidationError( + VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE, + MakeMessageWithArrayIndex( + "invalid handle in array expecting valid handles", + header->num_elements, + i).c_str()); + return false; + } + if (!bounds_checker->ClaimHandle(elements[i])) { + ReportValidationError(VALIDATION_ERROR_ILLEGAL_HANDLE); + return false; + } + } + return true; + } +}; + +template <typename H> +struct ArraySerializationHelper<H, true> { + typedef typename ArrayDataTraits<H>::StorageType ElementType; + + static void EncodePointersAndHandles(const ArrayHeader* header, + ElementType* elements, + std::vector<Handle>* handles) { + ArraySerializationHelper<Handle, true>::EncodePointersAndHandles( + header, elements, handles); + } + + static void DecodePointersAndHandles(const ArrayHeader* header, + ElementType* elements, + std::vector<Handle>* handles) { + ArraySerializationHelper<Handle, true>::DecodePointersAndHandles( + header, elements, handles); + } + + static bool ValidateElements(const ArrayHeader* header, + const ElementType* elements, + BoundsChecker* bounds_checker, + const ArrayValidateParams* validate_params) { + return ArraySerializationHelper<Handle, true>::ValidateElements( + header, elements, bounds_checker, validate_params); + } +}; + +template <typename P> +struct ArraySerializationHelper<P*, false> { + typedef typename ArrayDataTraits<P*>::StorageType ElementType; + + static void EncodePointersAndHandles(const ArrayHeader* header, + ElementType* elements, + std::vector<Handle>* handles) { + for (uint32_t i = 0; i < header->num_elements; ++i) + Encode(&elements[i], handles); + } + + static void DecodePointersAndHandles(const ArrayHeader* header, + ElementType* elements, + std::vector<Handle>* handles) { + for (uint32_t i = 0; i < header->num_elements; ++i) + Decode(&elements[i], handles); + } + + static bool ValidateElements(const ArrayHeader* header, + const ElementType* elements, + BoundsChecker* bounds_checker, + const ArrayValidateParams* validate_params) { + for (uint32_t i = 0; i < header->num_elements; ++i) { + if (!validate_params->element_is_nullable && !elements[i].offset) { + ReportValidationError( + VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, + MakeMessageWithArrayIndex("null in array expecting valid pointers", + header->num_elements, + i).c_str()); + return false; + } + if (!ValidateEncodedPointer(&elements[i].offset)) { + ReportValidationError(VALIDATION_ERROR_ILLEGAL_POINTER); + return false; + } + if (!ValidateCaller<P>::Run(DecodePointerRaw(&elements[i].offset), + bounds_checker, + validate_params->element_validate_params)) { + return false; + } + } + return true; + } + + private: + template <typename T, + bool is_union = IsUnionDataType<T>::value> + struct ValidateCaller {}; + + template <typename T> + struct ValidateCaller<T, false> { + static bool Run(const void* data, + BoundsChecker* bounds_checker, + const ArrayValidateParams* validate_params) { + MOJO_DCHECK(!validate_params) + << "Struct type should not have array validate params"; + + return T::Validate(data, bounds_checker); + } + }; + + template <typename T> + struct ValidateCaller<T, true> { + static bool Run(const void* data, + BoundsChecker* bounds_checker, + const ArrayValidateParams* validate_params) { + MOJO_DCHECK(!validate_params) + << "Union type should not have array validate params"; + + return T::Validate(data, bounds_checker, true); + } + }; + + template <typename Key, typename Value> + struct ValidateCaller<Map_Data<Key, Value>, false> { + static bool Run(const void* data, + BoundsChecker* bounds_checker, + const ArrayValidateParams* validate_params) { + return Map_Data<Key, Value>::Validate(data, bounds_checker, + validate_params); + } + }; + + template <typename T> + struct ValidateCaller<Array_Data<T>, false> { + static bool Run(const void* data, + BoundsChecker* bounds_checker, + const ArrayValidateParams* validate_params) { + return Array_Data<T>::Validate(data, bounds_checker, validate_params); + } + }; +}; + +template <typename T> +class Array_Data { + public: + typedef ArrayDataTraits<T> Traits; + typedef typename Traits::StorageType StorageType; + typedef typename Traits::Ref Ref; + typedef typename Traits::ConstRef ConstRef; + typedef ArraySerializationHelper<T, IsHandle<T>::value> Helper; + + // Returns null if |num_elements| or the corresponding storage size cannot be + // stored in uint32_t. + static Array_Data<T>* New(size_t num_elements, Buffer* buf) { + if (num_elements > Traits::kMaxNumElements) + return nullptr; + + uint32_t num_bytes = + Traits::GetStorageSize(static_cast<uint32_t>(num_elements)); + return new (buf->Allocate(num_bytes)) + Array_Data<T>(num_bytes, static_cast<uint32_t>(num_elements)); + } + + static bool Validate(const void* data, + BoundsChecker* bounds_checker, + const ArrayValidateParams* validate_params) { + if (!data) + return true; + if (!IsAligned(data)) { + ReportValidationError(VALIDATION_ERROR_MISALIGNED_OBJECT); + return false; + } + if (!bounds_checker->IsValidRange(data, sizeof(ArrayHeader))) { + ReportValidationError(VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE); + return false; + } + const ArrayHeader* header = static_cast<const ArrayHeader*>(data); + if (header->num_elements > Traits::kMaxNumElements || + header->num_bytes < Traits::GetStorageSize(header->num_elements)) { + ReportValidationError(VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER); + return false; + } + if (validate_params->expected_num_elements != 0 && + header->num_elements != validate_params->expected_num_elements) { + ReportValidationError( + VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER, + MakeMessageWithExpectedArraySize( + "fixed-size array has wrong number of elements", + header->num_elements, + validate_params->expected_num_elements).c_str()); + return false; + } + if (!bounds_checker->ClaimMemory(data, header->num_bytes)) { + ReportValidationError(VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE); + return false; + } + + const Array_Data<T>* object = static_cast<const Array_Data<T>*>(data); + return Helper::ValidateElements(&object->header_, object->storage(), + bounds_checker, validate_params); + } + + size_t size() const { return header_.num_elements; } + + Ref at(size_t offset) { + MOJO_DCHECK(offset < static_cast<size_t>(header_.num_elements)); + return Traits::ToRef(storage(), offset); + } + + ConstRef at(size_t offset) const { + MOJO_DCHECK(offset < static_cast<size_t>(header_.num_elements)); + return Traits::ToConstRef(storage(), offset); + } + + StorageType* storage() { + return reinterpret_cast<StorageType*>(reinterpret_cast<char*>(this) + + sizeof(*this)); + } + + const StorageType* storage() const { + return reinterpret_cast<const StorageType*>( + reinterpret_cast<const char*>(this) + sizeof(*this)); + } + + void EncodePointersAndHandles(std::vector<Handle>* handles) { + Helper::EncodePointersAndHandles(&header_, storage(), handles); + } + + void DecodePointersAndHandles(std::vector<Handle>* handles) { + Helper::DecodePointersAndHandles(&header_, storage(), handles); + } + + private: + Array_Data(uint32_t num_bytes, uint32_t num_elements) { + header_.num_bytes = num_bytes; + header_.num_elements = num_elements; + } + ~Array_Data() = delete; + + internal::ArrayHeader header_; + + // Elements of type internal::ArrayDataTraits<T>::StorageType follow. +}; +static_assert(sizeof(Array_Data<char>) == 8, "Bad sizeof(Array_Data)"); + +// UTF-8 encoded +typedef Array_Data<char> String_Data; + +template <typename T, bool kIsMoveOnlyType> +struct ArrayTraits {}; + +template <typename T> +struct ArrayTraits<T, false> { + typedef T StorageType; + typedef typename std::vector<T>::reference RefType; + typedef typename std::vector<T>::const_reference ConstRefType; + typedef ConstRefType ForwardType; + static inline void Initialize(std::vector<T>* vec) {} + static inline void Finalize(std::vector<T>* vec) {} + static inline ConstRefType at(const std::vector<T>* vec, size_t offset) { + return vec->at(offset); + } + static inline RefType at(std::vector<T>* vec, size_t offset) { + return vec->at(offset); + } + static inline void Resize(std::vector<T>* vec, size_t size) { + vec->resize(size); + } + static inline void PushBack(std::vector<T>* vec, ForwardType value) { + vec->push_back(value); + } + static inline void Clone(const std::vector<T>& src_vec, + std::vector<T>* dest_vec) { + dest_vec->assign(src_vec.begin(), src_vec.end()); + } +}; + +template <typename T> +struct ArrayTraits<T, true> { + struct StorageType { + char buf[sizeof(T) + (8 - (sizeof(T) % 8)) % 8]; // Make 8-byte aligned. + }; + typedef T& RefType; + typedef const T& ConstRefType; + typedef T ForwardType; + static inline void Initialize(std::vector<StorageType>* vec) { + for (size_t i = 0; i < vec->size(); ++i) + new (vec->at(i).buf) T(); + } + static inline void Finalize(std::vector<StorageType>* vec) { + for (size_t i = 0; i < vec->size(); ++i) + reinterpret_cast<T*>(vec->at(i).buf)->~T(); + } + static inline ConstRefType at(const std::vector<StorageType>* vec, + size_t offset) { + return *reinterpret_cast<const T*>(vec->at(offset).buf); + } + static inline RefType at(std::vector<StorageType>* vec, size_t offset) { + return *reinterpret_cast<T*>(vec->at(offset).buf); + } + static inline void Resize(std::vector<StorageType>* vec, size_t size) { + size_t old_size = vec->size(); + for (size_t i = size; i < old_size; i++) + reinterpret_cast<T*>(vec->at(i).buf)->~T(); + ResizeStorage(vec, size); + for (size_t i = old_size; i < vec->size(); i++) + new (vec->at(i).buf) T(); + } + static inline void PushBack(std::vector<StorageType>* vec, RefType value) { + size_t old_size = vec->size(); + ResizeStorage(vec, old_size + 1); + new (vec->at(old_size).buf) T(value.Pass()); + } + static inline void ResizeStorage(std::vector<StorageType>* vec, size_t size) { + if (size <= vec->capacity()) { + vec->resize(size); + return; + } + std::vector<StorageType> new_storage(size); + for (size_t i = 0; i < vec->size(); i++) + new (new_storage.at(i).buf) T(at(vec, i).Pass()); + vec->swap(new_storage); + Finalize(&new_storage); + } + static inline void Clone(const std::vector<StorageType>& src_vec, + std::vector<StorageType>* dest_vec) { + Resize(dest_vec, src_vec.size()); + for (size_t i = 0; i < src_vec.size(); ++i) + at(dest_vec, i) = at(&src_vec, i).Clone(); + } +}; + +template <> +struct WrapperTraits<String, false> { + typedef String_Data* DataType; +}; + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_INTERNAL_H_ diff --git a/mojo/public/cpp/bindings/lib/array_serialization.h b/mojo/public/cpp/bindings/lib/array_serialization.h new file mode 100644 index 0000000..9d0e7bb --- /dev/null +++ b/mojo/public/cpp/bindings/lib/array_serialization.h @@ -0,0 +1,338 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_SERIALIZATION_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_SERIALIZATION_H_ + +#include <string.h> // For |memcpy()|. + +#include <vector> + +#include "mojo/public/c/system/macros.h" +#include "mojo/public/cpp/bindings/lib/array_internal.h" +#include "mojo/public/cpp/bindings/lib/map_serialization.h" +#include "mojo/public/cpp/bindings/lib/string_serialization.h" +#include "mojo/public/cpp/bindings/lib/template_util.h" +#include "mojo/public/cpp/bindings/lib/validation_errors.h" + +namespace mojo { + +template <typename E> +inline size_t GetSerializedSize_(const Array<E>& input); + +template <typename E, typename F> +inline void SerializeArray_( + Array<E> input, + internal::Buffer* buf, + internal::Array_Data<F>** output, + const internal::ArrayValidateParams* validate_params); + +template <typename E, typename F> +inline void Deserialize_(internal::Array_Data<F>* data, Array<E>* output); + +namespace internal { + +template <typename E, + typename F, + bool is_union = + IsUnionDataType<typename RemovePointer<F>::type>::value> +struct ArraySerializer; + +// Handles serialization and deserialization of arrays of pod types. +template <typename E, typename F> +struct ArraySerializer<E, F, false> { + static_assert(sizeof(E) == sizeof(F), "Incorrect array serializer"); + static size_t GetSerializedSize(const Array<E>& input) { + return sizeof(Array_Data<F>) + Align(input.size() * sizeof(E)); + } + + static void SerializeElements(Array<E> input, + Buffer* buf, + Array_Data<F>* output, + const ArrayValidateParams* validate_params) { + MOJO_DCHECK(!validate_params->element_is_nullable) + << "Primitive type should be non-nullable"; + MOJO_DCHECK(!validate_params->element_validate_params) + << "Primitive type should not have array validate params"; + + if (input.size()) + memcpy(output->storage(), &input.storage()[0], input.size() * sizeof(E)); + } + static void DeserializeElements(Array_Data<F>* input, Array<E>* output) { + std::vector<E> result(input->size()); + if (input->size()) + memcpy(&result[0], input->storage(), input->size() * sizeof(E)); + output->Swap(&result); + } +}; + +// Serializes and deserializes arrays of bools. +template <> +struct ArraySerializer<bool, bool, false> { + static size_t GetSerializedSize(const Array<bool>& input) { + return sizeof(Array_Data<bool>) + Align((input.size() + 7) / 8); + } + + static void SerializeElements(Array<bool> input, + Buffer* buf, + Array_Data<bool>* output, + const ArrayValidateParams* validate_params) { + MOJO_DCHECK(!validate_params->element_is_nullable) + << "Primitive type should be non-nullable"; + MOJO_DCHECK(!validate_params->element_validate_params) + << "Primitive type should not have array validate params"; + + // TODO(darin): Can this be a memcpy somehow instead of a bit-by-bit copy? + for (size_t i = 0; i < input.size(); ++i) + output->at(i) = input[i]; + } + static void DeserializeElements(Array_Data<bool>* input, + Array<bool>* output) { + Array<bool> result(input->size()); + // TODO(darin): Can this be a memcpy somehow instead of a bit-by-bit copy? + for (size_t i = 0; i < input->size(); ++i) + result.at(i) = input->at(i); + output->Swap(&result); + } +}; + +// Serializes and deserializes arrays of handles. +template <typename H> +struct ArraySerializer<ScopedHandleBase<H>, H, false> { + static size_t GetSerializedSize(const Array<ScopedHandleBase<H>>& input) { + return sizeof(Array_Data<H>) + Align(input.size() * sizeof(H)); + } + + static void SerializeElements(Array<ScopedHandleBase<H>> input, + Buffer* buf, + Array_Data<H>* output, + const ArrayValidateParams* validate_params) { + MOJO_DCHECK(!validate_params->element_validate_params) + << "Handle type should not have array validate params"; + + for (size_t i = 0; i < input.size(); ++i) { + output->at(i) = input[i].release(); // Transfer ownership of the handle. + MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( + !validate_params->element_is_nullable && !output->at(i).is_valid(), + VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE, + MakeMessageWithArrayIndex( + "invalid handle in array expecting valid handles", input.size(), + i)); + } + } + static void DeserializeElements(Array_Data<H>* input, + Array<ScopedHandleBase<H>>* output) { + Array<ScopedHandleBase<H>> result(input->size()); + for (size_t i = 0; i < input->size(); ++i) + result.at(i) = MakeScopedHandle(FetchAndReset(&input->at(i))); + output->Swap(&result); + } +}; + +// This template must only apply to pointer mojo entity (structs and arrays). +// This is done by ensuring that WrapperTraits<S>::DataType is a pointer. +template <typename S> +struct ArraySerializer< + S, + typename EnableIf<IsPointer<typename WrapperTraits<S>::DataType>::value, + typename WrapperTraits<S>::DataType>::type, + false> { + typedef + typename RemovePointer<typename WrapperTraits<S>::DataType>::type S_Data; + static size_t GetSerializedSize(const Array<S>& input) { + size_t size = sizeof(Array_Data<S_Data*>) + + input.size() * sizeof(StructPointer<S_Data>); + for (size_t i = 0; i < input.size(); ++i) + size += GetSerializedSize_(input[i]); + return size; + } + + static void SerializeElements(Array<S> input, + Buffer* buf, + Array_Data<S_Data*>* output, + const ArrayValidateParams* validate_params) { + for (size_t i = 0; i < input.size(); ++i) { + S_Data* element; + SerializeCaller<S>::Run(input[i].Pass(), buf, &element, + validate_params->element_validate_params); + output->at(i) = element; + MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( + !validate_params->element_is_nullable && !element, + VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, + MakeMessageWithArrayIndex("null in array expecting valid pointers", + input.size(), i)); + } + } + static void DeserializeElements(Array_Data<S_Data*>* input, + Array<S>* output) { + Array<S> result(input->size()); + for (size_t i = 0; i < input->size(); ++i) { + Deserialize_(input->at(i), &result[i]); + } + output->Swap(&result); + } + + private: + template <typename T> + struct SerializeCaller { + static void Run(T input, + Buffer* buf, + typename WrapperTraits<T>::DataType* output, + const ArrayValidateParams* validate_params) { + MOJO_DCHECK(!validate_params) + << "Struct type should not have array validate params"; + + Serialize_(input.Pass(), buf, output); + } + }; + + template <typename T> + struct SerializeCaller<Array<T>> { + static void Run(Array<T> input, + Buffer* buf, + typename Array<T>::Data_** output, + const ArrayValidateParams* validate_params) { + SerializeArray_(input.Pass(), buf, output, validate_params); + } + }; + + template <typename T, typename U> + struct SerializeCaller<Map<T, U>> { + static void Run(Map<T, U> input, + Buffer* buf, + typename Map<T, U>::Data_** output, + const ArrayValidateParams* validate_params) { + SerializeMap_(input.Pass(), buf, output, validate_params); + } + }; +}; + +// Handles serialization and deserialization of arrays of unions. +template <typename U, typename U_Data> +struct ArraySerializer<U, U_Data, true> { + static size_t GetSerializedSize(const Array<U>& input) { + size_t size = sizeof(Array_Data<U_Data>); + for (size_t i = 0; i < input.size(); ++i) { + // GetSerializedSize_ will account for both the data in the union and the + // space in the array used to hold the union. + size += GetSerializedSize_(input[i], false); + } + return size; + } + + static void SerializeElements(Array<U> input, + Buffer* buf, + Array_Data<U_Data>* output, + const ArrayValidateParams* validate_params) { + for (size_t i = 0; i < input.size(); ++i) { + U_Data* result = output->storage() + i; + SerializeUnion_(input[i].Pass(), buf, &result, true); + MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( + !validate_params->element_is_nullable && output->at(i).is_null(), + VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, + MakeMessageWithArrayIndex("null in array expecting valid unions", + input.size(), i)); + } + } + + static void DeserializeElements(Array_Data<U_Data>* input, Array<U>* output) { + Array<U> result(input->size()); + for (size_t i = 0; i < input->size(); ++i) { + Deserialize_(&input->at(i), &result[i]); + } + output->Swap(&result); + } +}; + +// Handles serialization and deserialization of arrays of strings. +template <> +struct ArraySerializer<String, String_Data*> { + static size_t GetSerializedSize(const Array<String>& input) { + size_t size = + sizeof(Array_Data<String_Data*>) + input.size() * sizeof(StringPointer); + for (size_t i = 0; i < input.size(); ++i) + size += GetSerializedSize_(input[i]); + return size; + } + + static void SerializeElements(Array<String> input, + Buffer* buf, + Array_Data<String_Data*>* output, + const ArrayValidateParams* validate_params) { + MOJO_DCHECK( + validate_params->element_validate_params && + !validate_params->element_validate_params->element_validate_params && + !validate_params->element_validate_params->element_is_nullable && + validate_params->element_validate_params->expected_num_elements == 0) + << "String type has unexpected array validate params"; + + for (size_t i = 0; i < input.size(); ++i) { + String_Data* element; + Serialize_(input[i], buf, &element); + output->at(i) = element; + MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( + !validate_params->element_is_nullable && !element, + VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, + MakeMessageWithArrayIndex("null in array expecting valid strings", + input.size(), i)); + } + } + static void DeserializeElements(Array_Data<String_Data*>* input, + Array<String>* output) { + Array<String> result(input->size()); + for (size_t i = 0; i < input->size(); ++i) + Deserialize_(input->at(i), &result[i]); + output->Swap(&result); + } +}; + +} // namespace internal + +template <typename E> +inline size_t GetSerializedSize_(const Array<E>& input) { + if (!input) + return 0; + typedef typename internal::WrapperTraits<E>::DataType F; + return internal::ArraySerializer<E, F>::GetSerializedSize(input); +} + +template <typename E, typename F> +inline void SerializeArray_( + Array<E> input, + internal::Buffer* buf, + internal::Array_Data<F>** output, + const internal::ArrayValidateParams* validate_params) { + if (input) { + MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( + validate_params->expected_num_elements != 0 && + input.size() != validate_params->expected_num_elements, + internal::VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER, + internal::MakeMessageWithExpectedArraySize( + "fixed-size array has wrong number of elements", input.size(), + validate_params->expected_num_elements)); + + internal::Array_Data<F>* result = + internal::Array_Data<F>::New(input.size(), buf); + if (result) { + internal::ArraySerializer<E, F>::SerializeElements( + internal::Forward(input), buf, result, validate_params); + } + *output = result; + } else { + *output = nullptr; + } +} + +template <typename E, typename F> +inline void Deserialize_(internal::Array_Data<F>* input, Array<E>* output) { + if (input) { + internal::ArraySerializer<E, F>::DeserializeElements(input, output); + } else { + output->reset(); + } +} + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_SERIALIZATION_H_ diff --git a/mojo/public/cpp/bindings/lib/bindings_internal.h b/mojo/public/cpp/bindings/lib/bindings_internal.h new file mode 100644 index 0000000..436f580 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/bindings_internal.h @@ -0,0 +1,134 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_BINDINGS_INTERNAL_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_BINDINGS_INTERNAL_H_ + +#include "mojo/public/cpp/bindings/lib/template_util.h" +#include "mojo/public/cpp/bindings/struct_ptr.h" +#include "mojo/public/cpp/system/core.h" + +namespace mojo { +namespace internal { + +template <typename T> +class Array_Data; + +#pragma pack(push, 1) + +struct StructHeader { + uint32_t num_bytes; + uint32_t version; +}; +static_assert(sizeof(StructHeader) == 8, "Bad sizeof(StructHeader)"); + +struct ArrayHeader { + uint32_t num_bytes; + uint32_t num_elements; +}; +static_assert(sizeof(ArrayHeader) == 8, "Bad_sizeof(ArrayHeader)"); + +template <typename T> +union StructPointer { + uint64_t offset; + T* ptr; +}; +static_assert(sizeof(StructPointer<char>) == 8, "Bad_sizeof(StructPointer)"); + +template <typename T> +union ArrayPointer { + uint64_t offset; + Array_Data<T>* ptr; +}; +static_assert(sizeof(ArrayPointer<char>) == 8, "Bad_sizeof(ArrayPointer)"); + +union StringPointer { + uint64_t offset; + Array_Data<char>* ptr; +}; +static_assert(sizeof(StringPointer) == 8, "Bad_sizeof(StringPointer)"); + + +template <typename T> +union UnionPointer { + uint64_t offset; + T* ptr; +}; +static_assert(sizeof(UnionPointer<char>) == 8, "Bad_sizeof(UnionPointer)"); + +struct Interface_Data { + MessagePipeHandle handle; + uint32_t version; +}; +static_assert(sizeof(Interface_Data) == 8, "Bad_sizeof(Interface_Data)"); + +struct AssociatedInterface_Data { + uint32_t interface_id; + uint32_t version; +}; +static_assert(sizeof(AssociatedInterface_Data) == 8, + "Bad_sizeof(AssociatedInterface_Data)"); + +using AssociatedInterfaceRequest_Data = uint32_t; + +#pragma pack(pop) + +template <typename T> +void ResetIfNonNull(T* ptr) { + if (ptr) + *ptr = T(); +} + +template <typename T> +T FetchAndReset(T* ptr) { + T temp = *ptr; + *ptr = T(); + return temp; +} + +template <typename H> +struct IsHandle { + enum { value = IsBaseOf<Handle, H>::value }; +}; + +template <typename T> +struct IsUnionDataType { + template <typename U> + static YesType Test(const typename U::MojomUnionDataType*); + + template <typename U> + static NoType Test(...); + + static const bool value = + sizeof(Test<T>(0)) == sizeof(YesType) && !IsConst<T>::value; +}; + +template <typename T, bool move_only = IsMoveOnlyType<T>::value> +struct WrapperTraits; + +template <typename T> +struct WrapperTraits<T, false> { + typedef T DataType; +}; +template <typename H> +struct WrapperTraits<ScopedHandleBase<H>, true> { + typedef H DataType; +}; +template <typename S> +struct WrapperTraits<StructPtr<S>, true> { + typedef typename S::Data_* DataType; +}; +template <typename S> +struct WrapperTraits<InlinedStructPtr<S>, true> { + typedef typename S::Data_* DataType; +}; +template <typename S> +struct WrapperTraits<S, true> { + typedef typename S::Data_* DataType; +}; + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_BINDINGS_INTERNAL_H_ diff --git a/mojo/public/cpp/bindings/lib/bindings_serialization.cc b/mojo/public/cpp/bindings/lib/bindings_serialization.cc new file mode 100644 index 0000000..791afe2 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/bindings_serialization.cc @@ -0,0 +1,90 @@ +// 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. + +#include "mojo/public/cpp/bindings/lib/bindings_serialization.h" + +#include "mojo/public/cpp/environment/logging.h" + +namespace mojo { +namespace internal { + +namespace { + +const size_t kAlignment = 8; + +template <typename T> +T AlignImpl(T t) { + return t + (kAlignment - (t % kAlignment)) % kAlignment; +} + +} // namespace + +size_t Align(size_t size) { + return AlignImpl(size); +} + +char* AlignPointer(char* ptr) { + return reinterpret_cast<char*>(AlignImpl(reinterpret_cast<uintptr_t>(ptr))); +} + +bool IsAligned(const void* ptr) { + return !(reinterpret_cast<uintptr_t>(ptr) % kAlignment); +} + +void EncodePointer(const void* ptr, uint64_t* offset) { + if (!ptr) { + *offset = 0; + return; + } + + const char* p_obj = reinterpret_cast<const char*>(ptr); + const char* p_slot = reinterpret_cast<const char*>(offset); + MOJO_DCHECK(p_obj > p_slot); + + *offset = static_cast<uint64_t>(p_obj - p_slot); +} + +const void* DecodePointerRaw(const uint64_t* offset) { + if (!*offset) + return nullptr; + return reinterpret_cast<const char*>(offset) + *offset; +} + +void EncodeHandle(Handle* handle, std::vector<Handle>* handles) { + if (handle->is_valid()) { + handles->push_back(*handle); + handle->set_value(static_cast<MojoHandle>(handles->size() - 1)); + } else { + handle->set_value(kEncodedInvalidHandleValue); + } +} + +void EncodeHandle(Interface_Data* data, std::vector<Handle>* handles) { + EncodeHandle(&data->handle, handles); +} + +void EncodeHandle(MojoHandle* handle, std::vector<Handle>* handles) { + EncodeHandle(reinterpret_cast<Handle*>(handle), handles); +} + +void DecodeHandle(Handle* handle, std::vector<Handle>* handles) { + if (handle->value() == kEncodedInvalidHandleValue) { + *handle = Handle(); + return; + } + MOJO_DCHECK(handle->value() < handles->size()); + // Just leave holes in the vector so we don't screw up other indices. + *handle = FetchAndReset(&handles->at(handle->value())); +} + +void DecodeHandle(Interface_Data* data, std::vector<Handle>* handles) { + DecodeHandle(&data->handle, handles); +} + +void DecodeHandle(MojoHandle* handle, std::vector<Handle>* handles) { + DecodeHandle(reinterpret_cast<Handle*>(handle), handles); +} + +} // namespace internal +} // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/bindings_serialization.h b/mojo/public/cpp/bindings/lib/bindings_serialization.h new file mode 100644 index 0000000..5e2f9a7 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/bindings_serialization.h @@ -0,0 +1,91 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_BINDINGS_SERIALIZATION_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_BINDINGS_SERIALIZATION_H_ + +#include <vector> + +#include "mojo/public/cpp/bindings/interface_ptr.h" +#include "mojo/public/cpp/bindings/lib/bindings_internal.h" +#include "mojo/public/cpp/system/core.h" + +namespace mojo { +namespace internal { + +// Please note that this is a different value than |mojo::kInvalidHandleValue|, +// which is the "decoded" invalid handle. +const MojoHandle kEncodedInvalidHandleValue = static_cast<MojoHandle>(-1); + +size_t Align(size_t size); +char* AlignPointer(char* ptr); + +bool IsAligned(const void* ptr); + +// Pointers are encoded as relative offsets. The offsets are relative to the +// address of where the offset value is stored, such that the pointer may be +// recovered with the expression: +// +// ptr = reinterpret_cast<char*>(offset) + *offset +// +// A null pointer is encoded as an offset value of 0. +// +void EncodePointer(const void* ptr, uint64_t* offset); +// Note: This function doesn't validate the encoded pointer value. +const void* DecodePointerRaw(const uint64_t* offset); + +// Note: This function doesn't validate the encoded pointer value. +template <typename T> +inline void DecodePointer(const uint64_t* offset, T** ptr) { + *ptr = reinterpret_cast<T*>(const_cast<void*>(DecodePointerRaw(offset))); +} + +// Handles are encoded as indices into a vector of handles. These functions +// manipulate the value of |handle|, mapping it to and from an index. + +void EncodeHandle(Handle* handle, std::vector<Handle>* handles); +void EncodeHandle(Interface_Data* data, std::vector<Handle>* handles); +void EncodeHandle(MojoHandle* handle, std::vector<Handle>* handles); +// Note: The following three functions don't validate the encoded handle value. +void DecodeHandle(Handle* handle, std::vector<Handle>* handles); +void DecodeHandle(Interface_Data* data, std::vector<Handle>* handles); +void DecodeHandle(MojoHandle* handle, std::vector<Handle>* handles); + +// The following 2 functions are used to encode/decode all objects (structs and +// arrays) in a consistent manner. + +template <typename T> +inline void Encode(T* obj, std::vector<Handle>* handles) { + if (obj->ptr) + obj->ptr->EncodePointersAndHandles(handles); + EncodePointer(obj->ptr, &obj->offset); +} + +// Note: This function doesn't validate the encoded pointer and handle values. +template <typename T> +inline void Decode(T* obj, std::vector<Handle>* handles) { + DecodePointer(&obj->offset, &obj->ptr); + if (obj->ptr) + obj->ptr->DecodePointersAndHandles(handles); +} + +template <typename T> +inline void InterfacePointerToData(InterfacePtr<T> input, + Interface_Data* output) { + InterfacePtrInfo<T> info = input.PassInterface(); + output->handle = info.PassHandle().release(); + output->version = info.version(); +} + +template <typename T> +inline void InterfaceDataToPointer(Interface_Data* input, + InterfacePtr<T>* output) { + output->Bind(InterfacePtrInfo<T>( + MakeScopedHandle(FetchAndReset(&input->handle)), input->version)); +} + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_BINDINGS_SERIALIZATION_H_ diff --git a/mojo/public/cpp/bindings/lib/bounds_checker.cc b/mojo/public/cpp/bindings/lib/bounds_checker.cc new file mode 100644 index 0000000..5b96b2d --- /dev/null +++ b/mojo/public/cpp/bindings/lib/bounds_checker.cc @@ -0,0 +1,77 @@ +// 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. + +#include "mojo/public/cpp/bindings/lib/bounds_checker.h" + +#include "mojo/public/cpp/bindings/lib/bindings_serialization.h" +#include "mojo/public/cpp/environment/logging.h" +#include "mojo/public/cpp/system/handle.h" + +namespace mojo { +namespace internal { + +BoundsChecker::BoundsChecker(const void* data, + uint32_t data_num_bytes, + size_t num_handles) + : data_begin_(reinterpret_cast<uintptr_t>(data)), + data_end_(data_begin_ + data_num_bytes), + handle_begin_(0), + handle_end_(static_cast<uint32_t>(num_handles)) { + if (data_end_ < data_begin_) { + // The calculation of |data_end_| overflowed. + // It shouldn't happen but if it does, set the range to empty so + // IsValidRange() and ClaimMemory() always fail. + MOJO_DCHECK(false) << "Not reached"; + data_end_ = data_begin_; + } + if (handle_end_ < num_handles) { + // Assigning |num_handles| to |handle_end_| overflowed. + // It shouldn't happen but if it does, set the handle index range to empty. + MOJO_DCHECK(false) << "Not reached"; + handle_end_ = 0; + } +} + +BoundsChecker::~BoundsChecker() { +} + +bool BoundsChecker::ClaimMemory(const void* position, uint32_t num_bytes) { + uintptr_t begin = reinterpret_cast<uintptr_t>(position); + uintptr_t end = begin + num_bytes; + + if (!InternalIsValidRange(begin, end)) + return false; + + data_begin_ = end; + return true; +} + +bool BoundsChecker::ClaimHandle(const Handle& encoded_handle) { + uint32_t index = encoded_handle.value(); + if (index == kEncodedInvalidHandleValue) + return true; + + if (index < handle_begin_ || index >= handle_end_) + return false; + + // |index| + 1 shouldn't overflow, because |index| is not the max value of + // uint32_t (it is less than |handle_end_|). + handle_begin_ = index + 1; + return true; +} + +bool BoundsChecker::IsValidRange(const void* position, + uint32_t num_bytes) const { + uintptr_t begin = reinterpret_cast<uintptr_t>(position); + uintptr_t end = begin + num_bytes; + + return InternalIsValidRange(begin, end); +} + +bool BoundsChecker::InternalIsValidRange(uintptr_t begin, uintptr_t end) const { + return end > begin && begin >= data_begin_ && end <= data_end_; +} + +} // namespace internal +} // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/bounds_checker.h b/mojo/public/cpp/bindings/lib/bounds_checker.h new file mode 100644 index 0000000..f0520be --- /dev/null +++ b/mojo/public/cpp/bindings/lib/bounds_checker.h @@ -0,0 +1,63 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_BOUNDS_CHECKER_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_BOUNDS_CHECKER_H_ + +#include <stdint.h> + +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { + +class Handle; + +namespace internal { + +// BoundsChecker is used to validate object sizes, pointers and handle indices +// for payload of incoming messages. +class BoundsChecker { + public: + // [data, data + data_num_bytes) specifies the initial valid memory range. + // [0, num_handles) specifies the initial valid range of handle indices. + BoundsChecker(const void* data, uint32_t data_num_bytes, size_t num_handles); + + ~BoundsChecker(); + + // Claims the specified memory range. + // The method succeeds if the range is valid to claim. (Please see + // the comments for IsValidRange().) + // On success, the valid memory range is shrinked to begin right after the end + // of the claimed range. + bool ClaimMemory(const void* position, uint32_t num_bytes); + + // Claims the specified encoded handle (which is basically a handle index). + // The method succeeds if: + // - |encoded_handle|'s value is |kEncodedInvalidHandleValue|. + // - the handle is contained inside the valid range of handle indices. In this + // case, the valid range is shinked to begin right after the claimed handle. + bool ClaimHandle(const Handle& encoded_handle); + + // Returns true if the specified range is not empty, and the range is + // contained inside the valid memory range. + bool IsValidRange(const void* position, uint32_t num_bytes) const; + + private: + bool InternalIsValidRange(uintptr_t begin, uintptr_t end) const; + + // [data_begin_, data_end_) is the valid memory range. + uintptr_t data_begin_; + uintptr_t data_end_; + + // [handle_begin_, handle_end_) is the valid handle index range. + uint32_t handle_begin_; + uint32_t handle_end_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(BoundsChecker); +}; + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_BOUNDS_CHECKER_H_ diff --git a/mojo/public/cpp/bindings/lib/buffer.h b/mojo/public/cpp/bindings/lib/buffer.h new file mode 100644 index 0000000..c3b570e --- /dev/null +++ b/mojo/public/cpp/bindings/lib/buffer.h @@ -0,0 +1,24 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_BUFFER_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_BUFFER_H_ + +#include <stddef.h> + +namespace mojo { +namespace internal { + +// Buffer provides a way to allocate memory. Allocations are 8-byte aligned and +// zero-initialized. Allocations remain valid for the lifetime of the Buffer. +class Buffer { + public: + virtual ~Buffer() {} + virtual void* Allocate(size_t num_bytes) = 0; +}; + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_BUFFER_H_ diff --git a/mojo/public/cpp/bindings/lib/callback_internal.h b/mojo/public/cpp/bindings/lib/callback_internal.h new file mode 100644 index 0000000..9df5b40a7 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/callback_internal.h @@ -0,0 +1,53 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_CALLBACK_INTERNAL_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_CALLBACK_INTERNAL_H_ + +#include "mojo/public/cpp/bindings/lib/template_util.h" + +namespace mojo { +class String; + +namespace internal { + +template <typename T> +struct Callback_ParamTraits { + typedef T ForwardType; +}; + +template <> +struct Callback_ParamTraits<String> { + typedef const String& ForwardType; +}; + +template <typename T, typename... Args> +struct HasCompatibleCallOperator { + // This template's second parameter is the signature of the operator() + // overload we want to try to detect: + // void operator()(Args...) const; + template <typename U, + void (U::*)( + typename internal::Callback_ParamTraits<Args>::ForwardType...) + const> + struct TestType {}; + + // This matches type U if it has a call operator with the + // expected signature. + template <typename U> + static YesType Test(TestType<U, &U::operator()>*); + + // This matches anything else. + template <typename U> + static NoType Test(...); + + // HasCompatibleCallOperator<T, Args...>::value will be true if T has a + // compatible call operator. + enum { value = (sizeof(Test<T>(nullptr)) == sizeof(YesType)) }; +}; + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_CALLBACK_INTERNAL_H_ diff --git a/mojo/public/cpp/bindings/lib/connector.cc b/mojo/public/cpp/bindings/lib/connector.cc new file mode 100644 index 0000000..5bd94eb --- /dev/null +++ b/mojo/public/cpp/bindings/lib/connector.cc @@ -0,0 +1,259 @@ +// 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. + +#include "mojo/public/cpp/bindings/lib/connector.h" + +#include "mojo/public/cpp/environment/logging.h" + +namespace mojo { +namespace internal { + +// ---------------------------------------------------------------------------- + +Connector::Connector(ScopedMessagePipeHandle message_pipe, + const MojoAsyncWaiter* waiter) + : waiter_(waiter), + message_pipe_(message_pipe.Pass()), + incoming_receiver_(nullptr), + async_wait_id_(0), + error_(false), + drop_writes_(false), + enforce_errors_from_incoming_receiver_(true), + paused_(false), + destroyed_flag_(nullptr) { + // Even though we don't have an incoming receiver, we still want to monitor + // the message pipe to know if is closed or encounters an error. + WaitToReadMore(); +} + +Connector::~Connector() { + if (destroyed_flag_) + *destroyed_flag_ = true; + + CancelWait(); +} + +void Connector::CloseMessagePipe() { + CancelWait(); + Close(message_pipe_.Pass()); +} + +ScopedMessagePipeHandle Connector::PassMessagePipe() { + CancelWait(); + return message_pipe_.Pass(); +} + +void Connector::RaiseError() { + HandleError(true, true); +} + +bool Connector::WaitForIncomingMessage(MojoDeadline deadline) { + if (error_) + return false; + + ResumeIncomingMethodCallProcessing(); + + MojoResult rv = + Wait(message_pipe_.get(), MOJO_HANDLE_SIGNAL_READABLE, deadline, nullptr); + if (rv == MOJO_RESULT_SHOULD_WAIT || rv == MOJO_RESULT_DEADLINE_EXCEEDED) + return false; + if (rv != MOJO_RESULT_OK) { + // Users that call WaitForIncomingMessage() should expect their code to be + // re-entered, so we call the error handler synchronously. + HandleError(rv != MOJO_RESULT_FAILED_PRECONDITION, false); + return false; + } + mojo_ignore_result(ReadSingleMessage(&rv)); + return (rv == MOJO_RESULT_OK); +} + +void Connector::PauseIncomingMethodCallProcessing() { + if (paused_) + return; + + paused_ = true; + CancelWait(); +} + +void Connector::ResumeIncomingMethodCallProcessing() { + if (!paused_) + return; + + paused_ = false; + WaitToReadMore(); +} + +bool Connector::Accept(Message* message) { + if (error_) + return false; + + MOJO_CHECK(message_pipe_.is_valid()); + if (drop_writes_) + return true; + + MojoResult rv = + WriteMessageRaw(message_pipe_.get(), + message->data(), + message->data_num_bytes(), + message->mutable_handles()->empty() + ? nullptr + : reinterpret_cast<const MojoHandle*>( + &message->mutable_handles()->front()), + static_cast<uint32_t>(message->mutable_handles()->size()), + MOJO_WRITE_MESSAGE_FLAG_NONE); + + switch (rv) { + case MOJO_RESULT_OK: + // The handles were successfully transferred, so we don't need the message + // to track their lifetime any longer. + message->mutable_handles()->clear(); + break; + case MOJO_RESULT_FAILED_PRECONDITION: + // There's no point in continuing to write to this pipe since the other + // end is gone. Avoid writing any future messages. Hide write failures + // from the caller since we'd like them to continue consuming any backlog + // of incoming messages before regarding the message pipe as closed. + drop_writes_ = true; + break; + case MOJO_RESULT_BUSY: + // We'd get a "busy" result if one of the message's handles is: + // - |message_pipe_|'s own handle; + // - simultaneously being used on another thread; or + // - in a "busy" state that prohibits it from being transferred (e.g., + // a data pipe handle in the middle of a two-phase read/write, + // regardless of which thread that two-phase read/write is happening + // on). + // TODO(vtl): I wonder if this should be a |MOJO_DCHECK()|. (But, until + // crbug.com/389666, etc. are resolved, this will make tests fail quickly + // rather than hanging.) + MOJO_CHECK(false) << "Race condition or other bug detected"; + return false; + default: + // This particular write was rejected, presumably because of bad input. + // The pipe is not necessarily in a bad state. + return false; + } + return true; +} + +// static +void Connector::CallOnHandleReady(void* closure, MojoResult result) { + Connector* self = static_cast<Connector*>(closure); + self->OnHandleReady(result); +} + +void Connector::OnHandleReady(MojoResult result) { + MOJO_CHECK(async_wait_id_ != 0); + async_wait_id_ = 0; + if (result != MOJO_RESULT_OK) { + HandleError(result != MOJO_RESULT_FAILED_PRECONDITION, false); + return; + } + ReadAllAvailableMessages(); + // At this point, this object might have been deleted. Return. +} + +void Connector::WaitToReadMore() { + MOJO_CHECK(!async_wait_id_); + async_wait_id_ = waiter_->AsyncWait(message_pipe_.get().value(), + MOJO_HANDLE_SIGNAL_READABLE, + MOJO_DEADLINE_INDEFINITE, + &Connector::CallOnHandleReady, + this); +} + +bool Connector::ReadSingleMessage(MojoResult* read_result) { + bool receiver_result = false; + + // Detect if |this| was destroyed during message dispatch. Allow for the + // possibility of re-entering ReadMore() through message dispatch. + bool was_destroyed_during_dispatch = false; + bool* previous_destroyed_flag = destroyed_flag_; + destroyed_flag_ = &was_destroyed_during_dispatch; + + MojoResult rv = ReadAndDispatchMessage( + message_pipe_.get(), incoming_receiver_, &receiver_result); + if (read_result) + *read_result = rv; + + if (was_destroyed_during_dispatch) { + if (previous_destroyed_flag) + *previous_destroyed_flag = true; // Propagate flag. + return false; + } + destroyed_flag_ = previous_destroyed_flag; + + if (rv == MOJO_RESULT_SHOULD_WAIT) + return true; + + if (rv != MOJO_RESULT_OK) { + HandleError(rv != MOJO_RESULT_FAILED_PRECONDITION, false); + return false; + } + + if (enforce_errors_from_incoming_receiver_ && !receiver_result) { + HandleError(true, false); + return false; + } + return true; +} + +void Connector::ReadAllAvailableMessages() { + while (!error_) { + MojoResult rv; + + // Return immediately if |this| was destroyed. Do not touch any members! + if (!ReadSingleMessage(&rv)) + return; + + if (rv == MOJO_RESULT_SHOULD_WAIT) { + WaitToReadMore(); + break; + } + } +} + +void Connector::CancelWait() { + if (!async_wait_id_) + return; + + waiter_->CancelWait(async_wait_id_); + async_wait_id_ = 0; +} + +void Connector::HandleError(bool force_pipe_reset, bool force_async_handler) { + if (error_ || !message_pipe_.is_valid()) + return; + + if (!force_pipe_reset && force_async_handler) + force_pipe_reset = true; + + if (paused_) { + // If the user has paused receiving messages, we shouldn't call the error + // handler right away. We need to wait until the user starts receiving + // messages again. + force_async_handler = true; + } + + if (force_pipe_reset) { + CloseMessagePipe(); + MessagePipe dummy_pipe; + message_pipe_ = dummy_pipe.handle0.Pass(); + } else { + CancelWait(); + } + + if (force_async_handler) { + // |dummy_pipe.handle1| has been destructed. Reading the pipe will + // eventually cause a read error on |message_pipe_| and set error state. + if (!paused_) + WaitToReadMore(); + } else { + error_ = true; + connection_error_handler_.Run(); + } +} + +} // namespace internal +} // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/connector.h b/mojo/public/cpp/bindings/lib/connector.h new file mode 100644 index 0000000..185a984 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/connector.h @@ -0,0 +1,142 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_CONNECTOR_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_CONNECTOR_H_ + +#include "mojo/public/c/environment/async_waiter.h" +#include "mojo/public/cpp/bindings/callback.h" +#include "mojo/public/cpp/bindings/message.h" +#include "mojo/public/cpp/environment/environment.h" +#include "mojo/public/cpp/system/core.h" + +namespace mojo { +class ErrorHandler; + +namespace internal { + +// The Connector class is responsible for performing read/write operations on a +// MessagePipe. It writes messages it receives through the MessageReceiver +// interface that it subclasses, and it forwards messages it reads through the +// MessageReceiver interface assigned as its incoming receiver. +// +// NOTE: MessagePipe I/O is non-blocking. +// +class Connector : public MessageReceiver { + public: + // The Connector takes ownership of |message_pipe|. + explicit Connector( + ScopedMessagePipeHandle message_pipe, + const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()); + ~Connector() override; + + // Sets the receiver to handle messages read from the message pipe. The + // Connector will read messages from the pipe regardless of whether or not an + // incoming receiver has been set. + void set_incoming_receiver(MessageReceiver* receiver) { + incoming_receiver_ = receiver; + } + + // Errors from incoming receivers will force the connector into an error + // state, where no more messages will be processed. This method is used + // during testing to prevent that from happening. + void set_enforce_errors_from_incoming_receiver(bool enforce) { + enforce_errors_from_incoming_receiver_ = enforce; + } + + // Sets the error handler to receive notifications when an error is + // encountered while reading from the pipe or waiting to read from the pipe. + void set_connection_error_handler(const Closure& error_handler) { + connection_error_handler_ = error_handler; + } + + // Returns true if an error was encountered while reading from the pipe or + // waiting to read from the pipe. + bool encountered_error() const { return error_; } + + // Closes the pipe. The connector is put into a quiescent state. + // + // Please note that this method shouldn't be called unless it results from an + // explicit request of the user of bindings (e.g., the user sets an + // InterfacePtr to null or closes a Binding). + void CloseMessagePipe(); + + // Releases the pipe. Connector is put into a quiescent state. + ScopedMessagePipeHandle PassMessagePipe(); + + // Enters the error state. The upper layer may do this for unrecoverable + // issues such as invalid messages are received. If a connection error handler + // has been set, it will be called asynchronously. + // + // It is a no-op if the connector is already in the error state or there isn't + // a bound message pipe. Otherwise, it closes the message pipe, which notifies + // the other end and also prevents potential danger (say, the caller raises + // an error because it believes the other end is malicious). In order to + // appear to the user that the connector still binds to a message pipe, it + // creates a new message pipe, closes one end and binds to the other. + void RaiseError(); + + // Is the connector bound to a MessagePipe handle? + bool is_valid() const { return message_pipe_.is_valid(); } + + // Waits for the next message on the pipe, blocking until one arrives, + // |deadline| elapses, or an error happens. Returns |true| if a message has + // been delivered, |false| otherwise. + bool WaitForIncomingMessage(MojoDeadline deadline); + + // See Binding for details of pause/resume. + void PauseIncomingMethodCallProcessing(); + void ResumeIncomingMethodCallProcessing(); + + // MessageReceiver implementation: + bool Accept(Message* message) override; + + MessagePipeHandle handle() const { return message_pipe_.get(); } + + private: + static void CallOnHandleReady(void* closure, MojoResult result); + void OnHandleReady(MojoResult result); + + void WaitToReadMore(); + + // Returns false if |this| was destroyed during message dispatch. + MOJO_WARN_UNUSED_RESULT bool ReadSingleMessage(MojoResult* read_result); + + // |this| can be destroyed during message dispatch. + void ReadAllAvailableMessages(); + + // If |force_pipe_reset| is true, this method replaces the existing + // |message_pipe_| with a dummy message pipe handle (whose peer is closed). + // If |force_async_handler| is true, |connection_error_handler_| is called + // asynchronously. + void HandleError(bool force_pipe_reset, bool force_async_handler); + + // Cancels any calls made to |waiter_|. + void CancelWait(); + + Closure connection_error_handler_; + const MojoAsyncWaiter* waiter_; + + ScopedMessagePipeHandle message_pipe_; + MessageReceiver* incoming_receiver_; + + MojoAsyncWaitID async_wait_id_; + bool error_; + bool drop_writes_; + bool enforce_errors_from_incoming_receiver_; + + bool paused_; + + // If non-null, this will be set to true when the Connector is destroyed. We + // use this flag to allow for the Connector to be destroyed as a side-effect + // of dispatching an incoming message. + bool* destroyed_flag_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(Connector); +}; + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_CONNECTOR_H_ diff --git a/mojo/public/cpp/bindings/lib/control_message_handler.cc b/mojo/public/cpp/bindings/lib/control_message_handler.cc new file mode 100644 index 0000000..5113bb0 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/control_message_handler.cc @@ -0,0 +1,81 @@ +// Copyright 2015 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. + +#include "mojo/public/cpp/bindings/lib/control_message_handler.h" + +#include "mojo/public/cpp/bindings/lib/message_builder.h" +#include "mojo/public/cpp/environment/logging.h" +#include "mojo/public/interfaces/bindings/interface_control_messages.mojom.h" + +namespace mojo { +namespace internal { + +// static +bool ControlMessageHandler::IsControlMessage(const Message* message) { + return message->header()->name == kRunMessageId || + message->header()->name == kRunOrClosePipeMessageId; +} + +ControlMessageHandler::ControlMessageHandler(uint32_t interface_version) + : interface_version_(interface_version) { +} + +ControlMessageHandler::~ControlMessageHandler() { +} + +bool ControlMessageHandler::Accept(Message* message) { + if (message->header()->name == kRunOrClosePipeMessageId) + return RunOrClosePipe(message); + + MOJO_NOTREACHED(); + return false; +} + +bool ControlMessageHandler::AcceptWithResponder( + Message* message, + MessageReceiverWithStatus* responder) { + if (message->header()->name == kRunMessageId) + return Run(message, responder); + + MOJO_NOTREACHED(); + return false; +} + +bool ControlMessageHandler::Run(Message* message, + MessageReceiverWithStatus* responder) { + RunResponseMessageParamsPtr response_params_ptr( + RunResponseMessageParams::New()); + response_params_ptr->reserved0 = 16u; + response_params_ptr->reserved1 = 0u; + response_params_ptr->query_version_result = QueryVersionResult::New(); + response_params_ptr->query_version_result->version = interface_version_; + + size_t size = GetSerializedSize_(response_params_ptr); + ResponseMessageBuilder builder(kRunMessageId, size, message->request_id()); + + RunResponseMessageParams_Data* response_params = nullptr; + Serialize_(response_params_ptr.Pass(), builder.buffer(), &response_params); + response_params->EncodePointersAndHandles( + builder.message()->mutable_handles()); + bool ok = responder->Accept(builder.message()); + MOJO_ALLOW_UNUSED_LOCAL(ok); + delete responder; + + return true; +} + +bool ControlMessageHandler::RunOrClosePipe(Message* message) { + RunOrClosePipeMessageParams_Data* params = + reinterpret_cast<RunOrClosePipeMessageParams_Data*>( + message->mutable_payload()); + params->DecodePointersAndHandles(message->mutable_handles()); + + RunOrClosePipeMessageParamsPtr params_ptr; + Deserialize_(params, ¶ms_ptr); + + return interface_version_ >= params_ptr->require_version->version; +} + +} // namespace internal +} // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/control_message_handler.h b/mojo/public/cpp/bindings/lib/control_message_handler.h new file mode 100644 index 0000000..4a2fed5 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/control_message_handler.h @@ -0,0 +1,42 @@ +// Copyright 2015 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_LIB_CONTROL_MESSAGE_HANDLER_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_CONTROL_MESSAGE_HANDLER_H_ + +#include <stdint.h> + +#include "mojo/public/cpp/bindings/message.h" +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { +namespace internal { + +// Handlers for request messages defined in interface_control_messages.mojom. +class ControlMessageHandler : public MessageReceiverWithResponderStatus { + public: + static bool IsControlMessage(const Message* message); + + explicit ControlMessageHandler(uint32_t interface_version); + ~ControlMessageHandler() override; + + // Call the following methods only if IsControlMessage() returned true. + bool Accept(Message* message) override; + // Takes ownership of |responder|. + bool AcceptWithResponder(Message* message, + MessageReceiverWithStatus* responder) override; + + private: + bool Run(Message* message, MessageReceiverWithStatus* responder); + bool RunOrClosePipe(Message* message); + + uint32_t interface_version_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(ControlMessageHandler); +}; + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_CONTROL_MESSAGE_HANDLER_H_ diff --git a/mojo/public/cpp/bindings/lib/control_message_proxy.cc b/mojo/public/cpp/bindings/lib/control_message_proxy.cc new file mode 100644 index 0000000..ad729c5 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/control_message_proxy.cc @@ -0,0 +1,100 @@ +// Copyright 2015 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. + +#include "mojo/public/cpp/bindings/lib/control_message_proxy.h" + +#include "mojo/public/cpp/bindings/lib/message_builder.h" +#include "mojo/public/cpp/bindings/message.h" +#include "mojo/public/cpp/system/macros.h" +#include "mojo/public/interfaces/bindings/interface_control_messages.mojom.h" + +namespace mojo { +namespace internal { + +namespace { + +using RunCallback = Callback<void(QueryVersionResultPtr)>; + +class RunResponseForwardToCallback : public MessageReceiver { + public: + RunResponseForwardToCallback(const RunCallback& callback) + : callback_(callback) {} + bool Accept(Message* message) override; + + private: + RunCallback callback_; + MOJO_DISALLOW_COPY_AND_ASSIGN(RunResponseForwardToCallback); +}; + +bool RunResponseForwardToCallback::Accept(Message* message) { + RunResponseMessageParams_Data* params = + reinterpret_cast<RunResponseMessageParams_Data*>( + message->mutable_payload()); + params->DecodePointersAndHandles(message->mutable_handles()); + + RunResponseMessageParamsPtr params_ptr; + Deserialize_(params, ¶ms_ptr); + + callback_.Run(params_ptr->query_version_result.Pass()); + return true; +} + +void SendRunMessage(MessageReceiverWithResponder* receiver, + QueryVersionPtr query_version, + const RunCallback& callback) { + RunMessageParamsPtr params_ptr(RunMessageParams::New()); + params_ptr->reserved0 = 16u; + params_ptr->reserved1 = 0u; + params_ptr->query_version = query_version.Pass(); + + size_t size = GetSerializedSize_(params_ptr); + RequestMessageBuilder builder(kRunMessageId, size); + + RunMessageParams_Data* params = nullptr; + Serialize_(params_ptr.Pass(), builder.buffer(), ¶ms); + params->EncodePointersAndHandles(builder.message()->mutable_handles()); + MessageReceiver* responder = new RunResponseForwardToCallback(callback); + if (!receiver->AcceptWithResponder(builder.message(), responder)) + delete responder; +} + +void SendRunOrClosePipeMessage(MessageReceiverWithResponder* receiver, + RequireVersionPtr require_version) { + RunOrClosePipeMessageParamsPtr params_ptr(RunOrClosePipeMessageParams::New()); + params_ptr->reserved0 = 16u; + params_ptr->reserved1 = 0u; + params_ptr->require_version = require_version.Pass(); + + size_t size = GetSerializedSize_(params_ptr); + MessageBuilder builder(kRunOrClosePipeMessageId, size); + + RunOrClosePipeMessageParams_Data* params = nullptr; + Serialize_(params_ptr.Pass(), builder.buffer(), ¶ms); + params->EncodePointersAndHandles(builder.message()->mutable_handles()); + bool ok = receiver->Accept(builder.message()); + MOJO_ALLOW_UNUSED_LOCAL(ok); +} + +} // namespace + +ControlMessageProxy::ControlMessageProxy(MessageReceiverWithResponder* receiver) + : receiver_(receiver) { +} + +void ControlMessageProxy::QueryVersion( + const Callback<void(uint32_t)>& callback) { + auto run_callback = [callback](QueryVersionResultPtr query_version_result) { + callback.Run(query_version_result->version); + }; + SendRunMessage(receiver_, QueryVersion::New(), run_callback); +} + +void ControlMessageProxy::RequireVersion(uint32_t version) { + RequireVersionPtr require_version(RequireVersion::New()); + require_version->version = version; + SendRunOrClosePipeMessage(receiver_, require_version.Pass()); +} + +} // namespace internal +} // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/control_message_proxy.h b/mojo/public/cpp/bindings/lib/control_message_proxy.h new file mode 100644 index 0000000..5b0f018 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/control_message_proxy.h @@ -0,0 +1,38 @@ +// Copyright 2015 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_LIB_CONTROL_MESSAGE_PROXY_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_CONTROL_MESSAGE_PROXY_H_ + +#include <stdint.h> + +#include "mojo/public/cpp/bindings/callback.h" +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { + +class MessageReceiverWithResponder; + +namespace internal { + +// Proxy for request messages defined in interface_control_messages.mojom. +class ControlMessageProxy { + public: + // Doesn't take ownership of |receiver|. It must outlive this object. + explicit ControlMessageProxy(MessageReceiverWithResponder* receiver); + + void QueryVersion(const Callback<void(uint32_t)>& callback); + void RequireVersion(uint32_t version); + + protected: + // Not owned. + MessageReceiverWithResponder* receiver_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(ControlMessageProxy); +}; + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_CONTROL_MESSAGE_PROXY_H_ diff --git a/mojo/public/cpp/bindings/lib/filter_chain.cc b/mojo/public/cpp/bindings/lib/filter_chain.cc new file mode 100644 index 0000000..d32eb78 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/filter_chain.cc @@ -0,0 +1,49 @@ +// 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. + +#include "mojo/public/cpp/bindings/lib/filter_chain.h" + +#include <algorithm> + +#include "mojo/public/cpp/environment/logging.h" + +namespace mojo { +namespace internal { + +FilterChain::FilterChain(MessageReceiver* sink) : sink_(sink) { +} + +FilterChain::FilterChain(FilterChain&& other) : sink_(other.sink_) { + other.sink_ = nullptr; + filters_.swap(other.filters_); +} + +FilterChain& FilterChain::operator=(FilterChain&& other) { + std::swap(sink_, other.sink_); + filters_.swap(other.filters_); + return *this; +} + +FilterChain::~FilterChain() { + for (std::vector<MessageFilter*>::iterator iter = filters_.begin(); + iter != filters_.end(); + ++iter) { + delete *iter; + } +} + +void FilterChain::SetSink(MessageReceiver* sink) { + MOJO_DCHECK(!sink_); + sink_ = sink; + if (!filters_.empty()) + filters_.back()->set_sink(sink); +} + +MessageReceiver* FilterChain::GetHead() { + MOJO_DCHECK(sink_); + return filters_.empty() ? sink_ : filters_.front(); +} + +} // namespace internal +} // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/filter_chain.h b/mojo/public/cpp/bindings/lib/filter_chain.h new file mode 100644 index 0000000..bd7f9f5 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/filter_chain.h @@ -0,0 +1,64 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_FILTER_CHAIN_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_FILTER_CHAIN_H_ + +#include <vector> + +#include "mojo/public/cpp/bindings/message.h" +#include "mojo/public/cpp/bindings/message_filter.h" +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { +namespace internal { + +class FilterChain { + MOJO_MOVE_ONLY_TYPE(FilterChain) + + public: + // Doesn't take ownership of |sink|. Therefore |sink| has to stay alive while + // this object is alive. + explicit FilterChain(MessageReceiver* sink = nullptr); + + FilterChain(FilterChain&& other); + FilterChain& operator=(FilterChain&& other); + ~FilterChain(); + + template <typename FilterType> + inline void Append(); + + // Doesn't take ownership of |sink|. Therefore |sink| has to stay alive while + // this object is alive. + void SetSink(MessageReceiver* sink); + + // Returns a receiver to accept messages. Messages flow through all filters in + // the same order as they were appended to the chain. If all filters allow a + // message to pass, it will be forwarded to |sink_|. + // The returned value is invalidated when this object goes away. + MessageReceiver* GetHead(); + + private: + // Owned by this object. + std::vector<MessageFilter*> filters_; + + MessageReceiver* sink_; +}; + +template <typename FilterType> +inline void FilterChain::Append() { + FilterType* filter = new FilterType(sink_); + if (!filters_.empty()) + filters_.back()->set_sink(filter); + filters_.push_back(filter); +} + +template <> +inline void FilterChain::Append<PassThroughFilter>() { +} + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_FILTER_CHAIN_H_ diff --git a/mojo/public/cpp/bindings/lib/fixed_buffer.cc b/mojo/public/cpp/bindings/lib/fixed_buffer.cc new file mode 100644 index 0000000..c81fc6e --- /dev/null +++ b/mojo/public/cpp/bindings/lib/fixed_buffer.cc @@ -0,0 +1,60 @@ +// 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. + +#include "mojo/public/cpp/bindings/lib/fixed_buffer.h" + +#include <stdlib.h> + +#include <algorithm> + +#include "mojo/public/cpp/bindings/lib/bindings_serialization.h" +#include "mojo/public/cpp/environment/logging.h" + +namespace mojo { +namespace internal { + +FixedBuffer::FixedBuffer() : ptr_(nullptr), cursor_(0), size_(0) {} + +void FixedBuffer::Initialize(void* memory, size_t size) { + MOJO_DCHECK(size == internal::Align(size)); + + ptr_ = static_cast<char*>(memory); + cursor_ = 0; + size_ = size; +} + +void* FixedBuffer::Allocate(size_t delta) { + delta = internal::Align(delta); + + if (delta == 0 || delta > size_ - cursor_) { + MOJO_DCHECK(false) << "Not reached"; + return nullptr; + } + + char* result = ptr_ + cursor_; + cursor_ += delta; + + return result; +} + +FixedBufferForTesting::FixedBufferForTesting(size_t size) { + size_ = internal::Align(size); + // Use calloc here to ensure all message memory is zero'd out. + ptr_ = static_cast<char*>(calloc(size_, 1)); +} + +FixedBufferForTesting::~FixedBufferForTesting() { + free(ptr_); +} + +void* FixedBufferForTesting::Leak() { + char* ptr = ptr_; + ptr_ = nullptr; + cursor_ = 0; + size_ = 0; + return ptr; +} + +} // namespace internal +} // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/fixed_buffer.h b/mojo/public/cpp/bindings/lib/fixed_buffer.h new file mode 100644 index 0000000..83eaf97 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/fixed_buffer.h @@ -0,0 +1,78 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_FIXED_BUFFER_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_FIXED_BUFFER_H_ + +#include "mojo/public/cpp/bindings/lib/buffer.h" +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { +namespace internal { + +// FixedBuffer provides a simple way to allocate objects within a fixed chunk +// of memory. Objects are allocated by calling the |Allocate| method, which +// extends the buffer accordingly. Objects allocated in this way are not freed +// explicitly. Instead, they remain valid so long as the FixedBuffer remains +// valid. The Leak method may be used to steal the underlying memory from the +// FixedBuffer. +// +// Typical usage: +// +// { +// FixedBuffer buf(8 + 8); +// +// int* a = static_cast<int*>(buf->Allocate(sizeof(int))); +// *a = 2; +// +// double* b = static_cast<double*>(buf->Allocate(sizeof(double))); +// *b = 3.14f; +// +// void* data = buf.Leak(); +// Process(data); +// +// free(data); +// } + +class FixedBuffer : public Buffer { + public: + FixedBuffer(); + + // |size| should be aligned using internal::Align. + void Initialize(void* memory, size_t size); + + size_t size() const { return size_; } + + // Grows the buffer by |num_bytes| and returns a pointer to the start of the + // addition. The resulting address is 8-byte aligned, and the content of the + // memory is zero-filled. + void* Allocate(size_t num_bytes) override; + + protected: + char* ptr_; + size_t cursor_; + size_t size_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(FixedBuffer); +}; + +class FixedBufferForTesting : public FixedBuffer { + public: + explicit FixedBufferForTesting(size_t size); + ~FixedBufferForTesting() override; + + // Returns the internal memory owned by the Buffer to the caller. The Buffer + // relinquishes its pointer, effectively resetting the state of the Buffer + // and leaving the caller responsible for freeing the returned memory address + // when no longer needed. + void* Leak(); + + private: + MOJO_DISALLOW_COPY_AND_ASSIGN(FixedBufferForTesting); +}; + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_FIXED_BUFFER_H_ diff --git a/mojo/public/cpp/bindings/lib/interface_ptr_internal.h b/mojo/public/cpp/bindings/lib/interface_ptr_internal.h new file mode 100644 index 0000000..1c2cad8 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/interface_ptr_internal.h @@ -0,0 +1,174 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_INTERFACE_PTR_INTERNAL_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_INTERFACE_PTR_INTERNAL_H_ + +#include <algorithm> // For |std::swap()|. + +#include "mojo/public/cpp/bindings/callback.h" +#include "mojo/public/cpp/bindings/interface_ptr_info.h" +#include "mojo/public/cpp/bindings/lib/control_message_proxy.h" +#include "mojo/public/cpp/bindings/lib/filter_chain.h" +#include "mojo/public/cpp/bindings/lib/message_header_validator.h" +#include "mojo/public/cpp/bindings/lib/router.h" +#include "mojo/public/cpp/environment/logging.h" + +struct MojoAsyncWaiter; + +namespace mojo { +namespace internal { + +template <typename Interface> +class InterfacePtrState { + public: + InterfacePtrState() + : proxy_(nullptr), router_(nullptr), waiter_(nullptr), version_(0u) {} + + ~InterfacePtrState() { + // Destruction order matters here. We delete |proxy_| first, even though + // |router_| may have a reference to it, so that destructors for any request + // callbacks still pending can interact with the InterfacePtr. + delete proxy_; + delete router_; + } + + Interface* instance() { + ConfigureProxyIfNecessary(); + + // This will be null if the object is not bound. + return proxy_; + } + + uint32_t version() const { return version_; } + + void QueryVersion(const Callback<void(uint32_t)>& callback) { + ConfigureProxyIfNecessary(); + + // It is safe to capture |this| because the callback won't be run after this + // object goes away. + auto callback_wrapper = [this, callback](uint32_t version) { + this->version_ = version; + callback.Run(version); + }; + + // Do a static cast in case the interface contains methods with the same + // name. + static_cast<ControlMessageProxy*>(proxy_)->QueryVersion(callback_wrapper); + } + + void RequireVersion(uint32_t version) { + ConfigureProxyIfNecessary(); + + if (version <= version_) + return; + + version_ = version; + // Do a static cast in case the interface contains methods with the same + // name. + static_cast<ControlMessageProxy*>(proxy_)->RequireVersion(version); + } + + void Swap(InterfacePtrState* other) { + using std::swap; + swap(other->proxy_, proxy_); + swap(other->router_, router_); + handle_.swap(other->handle_); + swap(other->waiter_, waiter_); + swap(other->version_, version_); + } + + void Bind(InterfacePtrInfo<Interface> info, const MojoAsyncWaiter* waiter) { + MOJO_DCHECK(!proxy_); + MOJO_DCHECK(!router_); + MOJO_DCHECK(!handle_.is_valid()); + MOJO_DCHECK(!waiter_); + MOJO_DCHECK(version_ == 0u); + MOJO_DCHECK(info.is_valid()); + + handle_ = info.PassHandle(); + waiter_ = waiter; + version_ = info.version(); + } + + bool WaitForIncomingResponse() { + ConfigureProxyIfNecessary(); + + MOJO_DCHECK(router_); + return router_->WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE); + } + + // After this method is called, the object is in an invalid state and + // shouldn't be reused. + InterfacePtrInfo<Interface> PassInterface() { + return InterfacePtrInfo<Interface>( + router_ ? router_->PassMessagePipe() : handle_.Pass(), version_); + } + + bool is_bound() const { return handle_.is_valid() || router_; } + + bool encountered_error() const { + return router_ ? router_->encountered_error() : false; + } + + void set_connection_error_handler(const Closure& error_handler) { + ConfigureProxyIfNecessary(); + + MOJO_DCHECK(router_); + router_->set_connection_error_handler(error_handler); + } + + // Returns true if bound and awaiting a response to a message. + bool has_pending_callbacks() const { + return router_ && router_->has_pending_responders(); + } + + Router* router_for_testing() { + ConfigureProxyIfNecessary(); + return router_; + } + + private: + using Proxy = typename Interface::Proxy_; + + void ConfigureProxyIfNecessary() { + // The proxy has been configured. + if (proxy_) { + MOJO_DCHECK(router_); + return; + } + // The object hasn't been bound. + if (!waiter_) { + MOJO_DCHECK(!handle_.is_valid()); + return; + } + + FilterChain filters; + filters.Append<MessageHeaderValidator>(); + filters.Append<typename Interface::ResponseValidator_>(); + + router_ = new Router(handle_.Pass(), filters.Pass(), waiter_); + waiter_ = nullptr; + + proxy_ = new Proxy(router_); + } + + Proxy* proxy_; + Router* router_; + + // |proxy_| and |router_| are not initialized until read/write with the + // message pipe handle is needed. |handle_| and |waiter_| are valid between + // the Bind() call and the initialization of |proxy_| and |router_|. + ScopedMessagePipeHandle handle_; + const MojoAsyncWaiter* waiter_; + + uint32_t version_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(InterfacePtrState); +}; + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_INTERFACE_PTR_INTERNAL_H_ diff --git a/mojo/public/cpp/bindings/lib/map_data_internal.h b/mojo/public/cpp/bindings/lib/map_data_internal.h new file mode 100644 index 0000000..8315dbc --- /dev/null +++ b/mojo/public/cpp/bindings/lib/map_data_internal.h @@ -0,0 +1,141 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_MAP_DATA_INTERNAL_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MAP_DATA_INTERNAL_H_ + +#include "mojo/public/cpp/bindings/lib/array_internal.h" +#include "mojo/public/cpp/bindings/lib/validate_params.h" +#include "mojo/public/cpp/bindings/lib/validation_errors.h" +#include "mojo/public/cpp/bindings/lib/validation_util.h" + +namespace mojo { +namespace internal { + +inline const ArrayValidateParams* GetMapKeyValidateParamsDefault() { + // The memory allocated here never gets released to not cause an exit time + // destructor. + static const ArrayValidateParams* validate_params = + new ArrayValidateParams(0, false, nullptr); + return validate_params; +} + +inline const ArrayValidateParams* GetMapKeyValidateParamsForStrings() { + // The memory allocated here never gets released to not cause an exit time + // destructor. + static const ArrayValidateParams* validate_params = new ArrayValidateParams( + 0, false, new ArrayValidateParams(0, false, nullptr)); + return validate_params; +} + +template <typename MapKey> +struct MapKeyValidateParamsFactory { + static const ArrayValidateParams* Get() { + return GetMapKeyValidateParamsDefault(); + } +}; + +// For non-nullable strings only. (Which is OK; map keys can't be null.) +template <> +struct MapKeyValidateParamsFactory<mojo::internal::Array_Data<char>*> { + static const ArrayValidateParams* Get() { + return GetMapKeyValidateParamsForStrings(); + } +}; + +// Map serializes into a struct which has two arrays as struct fields, the keys +// and the values. +template <typename Key, typename Value> +class Map_Data { + public: + static Map_Data* New(Buffer* buf) { + return new (buf->Allocate(sizeof(Map_Data))) Map_Data(); + } + + static bool Validate(const void* data, + BoundsChecker* bounds_checker, + const ArrayValidateParams* value_validate_params) { + if (!data) + return true; + + if (!ValidateStructHeaderAndClaimMemory(data, bounds_checker)) + return false; + + const Map_Data* object = static_cast<const Map_Data*>(data); + if (object->header_.num_bytes != sizeof(Map_Data) || + object->header_.version != 0) { + ReportValidationError(VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER); + return false; + } + + if (!ValidateEncodedPointer(&object->keys.offset)) { + ReportValidationError(VALIDATION_ERROR_ILLEGAL_POINTER); + return false; + } + if (!object->keys.offset) { + ReportValidationError(VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, + "null key array in map struct"); + return false; + } + const ArrayValidateParams* key_validate_params = + MapKeyValidateParamsFactory<Key>::Get(); + if (!Array_Data<Key>::Validate(DecodePointerRaw(&object->keys.offset), + bounds_checker, key_validate_params)) { + return false; + } + + if (!ValidateEncodedPointer(&object->values.offset)) { + ReportValidationError(VALIDATION_ERROR_ILLEGAL_POINTER); + return false; + } + if (!object->values.offset) { + ReportValidationError(VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, + "null value array in map struct"); + return false; + } + if (!Array_Data<Value>::Validate(DecodePointerRaw(&object->values.offset), + bounds_checker, value_validate_params)) { + return false; + } + + const ArrayHeader* key_header = + static_cast<const ArrayHeader*>(DecodePointerRaw(&object->keys.offset)); + const ArrayHeader* value_header = static_cast<const ArrayHeader*>( + DecodePointerRaw(&object->values.offset)); + if (key_header->num_elements != value_header->num_elements) { + ReportValidationError(VALIDATION_ERROR_DIFFERENT_SIZED_ARRAYS_IN_MAP); + return false; + } + + return true; + } + + StructHeader header_; + + ArrayPointer<Key> keys; + ArrayPointer<Value> values; + + void EncodePointersAndHandles(std::vector<mojo::Handle>* handles) { + Encode(&keys, handles); + Encode(&values, handles); + } + + void DecodePointersAndHandles(std::vector<mojo::Handle>* handles) { + Decode(&keys, handles); + Decode(&values, handles); + } + + private: + Map_Data() { + header_.num_bytes = sizeof(*this); + header_.version = 0; + } + ~Map_Data() = delete; +}; +static_assert(sizeof(Map_Data<char, char>) == 24, "Bad sizeof(Map_Data)"); + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_MAP_DATA_INTERNAL_H_ diff --git a/mojo/public/cpp/bindings/lib/map_internal.h b/mojo/public/cpp/bindings/lib/map_internal.h new file mode 100644 index 0000000..84f927c --- /dev/null +++ b/mojo/public/cpp/bindings/lib/map_internal.h @@ -0,0 +1,220 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_MAP_INTERNAL_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MAP_INTERNAL_H_ + +#include <map> + +#include "mojo/public/cpp/bindings/array.h" +#include "mojo/public/cpp/bindings/lib/template_util.h" + +namespace mojo { +namespace internal { + +template <typename Key, typename Value, bool kValueIsMoveOnlyType> +struct MapTraits {}; + +// Defines traits of a map for which Value is not a move-only type. +template <typename Key, typename Value> +struct MapTraits<Key, Value, false> { + // Map keys can't be move only types. + static_assert(!internal::IsMoveOnlyType<Key>::value, + "Map keys cannot be move only types."); + + typedef Key KeyStorageType; + typedef Key& KeyRefType; + typedef const Key& KeyConstRefType; + typedef KeyConstRefType KeyForwardType; + + typedef Value ValueStorageType; + typedef Value& ValueRefType; + typedef const Value& ValueConstRefType; + typedef ValueConstRefType ValueForwardType; + + static inline void InitializeFrom( + std::map<KeyStorageType, ValueStorageType>* m, + mojo::Array<Key> keys, + mojo::Array<Value> values) { + for (size_t i = 0; i < keys.size(); ++i) + Insert(m, keys[i], values[i]); + } + static inline void Decompose(std::map<KeyStorageType, ValueStorageType>* m, + mojo::Array<Key>* keys, + mojo::Array<Value>* values) { + keys->resize(m->size()); + values->resize(m->size()); + int i = 0; + for (typename std::map<KeyStorageType, ValueStorageType>::iterator + it = m->begin(); + it != m->end(); + ++it, ++i) { + (*keys)[i] = it->first; + (*values)[i] = it->second; + } + } + static inline void Finalize(std::map<KeyStorageType, ValueStorageType>* m) {} + static inline ValueRefType at(std::map<KeyStorageType, ValueStorageType>* m, + KeyForwardType key) { + // We don't have C++11 library support yet, so we have to emulate the crash + // on a non-existent key. + auto it = m->find(key); + MOJO_CHECK(it != m->end()); + return it->second; + } + static inline ValueConstRefType at( + const std::map<KeyStorageType, ValueStorageType>* m, + KeyForwardType key) { + // We don't have C++11 library support yet, so we have to emulate the crash + // on a non-existent key. + auto it = m->find(key); + MOJO_CHECK(it != m->end()); + return it->second; + } + static inline ValueRefType GetOrInsert( + std::map<KeyStorageType, ValueStorageType>* m, + KeyForwardType key) { + // This is the backing for the index operator (operator[]). + return (*m)[key]; + } + static inline void Insert(std::map<KeyStorageType, ValueStorageType>* m, + KeyForwardType key, + ValueForwardType value) { + m->insert(std::make_pair(key, value)); + } + static inline KeyConstRefType GetKey( + const typename std::map<KeyStorageType, ValueStorageType>::const_iterator& + it) { + return it->first; + } + static inline ValueConstRefType GetValue( + const typename std::map<KeyStorageType, ValueStorageType>::const_iterator& + it) { + return it->second; + } + static inline ValueRefType GetValue( + const typename std::map<KeyStorageType, ValueStorageType>::iterator& it) { + return it->second; + } + static inline void Clone( + const std::map<KeyStorageType, ValueStorageType>& src, + std::map<KeyStorageType, ValueStorageType>* dst) { + dst->clear(); + for (auto it = src.begin(); it != src.end(); ++it) + dst->insert(*it); + } +}; + +// Defines traits of a map for which Value is a move-only type. +template <typename Key, typename Value> +struct MapTraits<Key, Value, true> { + // Map keys can't be move only types. + static_assert(!internal::IsMoveOnlyType<Key>::value, + "Map keys cannot be move only types."); + + typedef Key KeyStorageType; + typedef Key& KeyRefType; + typedef const Key& KeyConstRefType; + typedef KeyConstRefType KeyForwardType; + + struct ValueStorageType { + // Make 8-byte aligned. + char buf[sizeof(Value) + (8 - (sizeof(Value) % 8)) % 8]; + }; + typedef Value& ValueRefType; + typedef const Value& ValueConstRefType; + typedef Value ValueForwardType; + + static inline void InitializeFrom( + std::map<KeyStorageType, ValueStorageType>* m, + mojo::Array<Key> keys, + mojo::Array<Value> values) { + for (size_t i = 0; i < keys.size(); ++i) + Insert(m, keys[i], values[i]); + } + static inline void Decompose(std::map<KeyStorageType, ValueStorageType>* m, + mojo::Array<Key>* keys, + mojo::Array<Value>* values) { + keys->resize(m->size()); + values->resize(m->size()); + int i = 0; + for (typename std::map<KeyStorageType, ValueStorageType>::iterator + it = m->begin(); + it != m->end(); + ++it, ++i) { + (*keys)[i] = it->first; + (*values)[i] = GetValue(it).Pass(); + } + } + static inline void Finalize(std::map<KeyStorageType, ValueStorageType>* m) { + for (auto& pair : *m) + reinterpret_cast<Value*>(pair.second.buf)->~Value(); + } + static inline ValueRefType at(std::map<KeyStorageType, ValueStorageType>* m, + KeyForwardType key) { + // We don't have C++11 library support yet, so we have to emulate the crash + // on a non-existent key. + auto it = m->find(key); + MOJO_CHECK(it != m->end()); + return GetValue(it); + } + static inline ValueConstRefType at( + const std::map<KeyStorageType, ValueStorageType>* m, + KeyForwardType key) { + // We don't have C++11 library support yet, so we have to emulate the crash + // on a non-existent key. + auto it = m->find(key); + MOJO_CHECK(it != m->end()); + return GetValue(it); + } + static inline ValueRefType GetOrInsert( + std::map<KeyStorageType, ValueStorageType>* m, + KeyForwardType key) { + // This is the backing for the index operator (operator[]). + auto it = m->find(key); + if (it == m->end()) { + it = m->insert(std::make_pair(key, ValueStorageType())).first; + new (it->second.buf) Value(); + } + + return GetValue(it); + } + static inline void Insert(std::map<KeyStorageType, ValueStorageType>* m, + KeyForwardType key, + ValueRefType value) { + // STL insert() doesn't insert |value| if |key| is already part of |m|. We + // have to use operator[] to initialize into the storage buffer, but we + // have to do a manual check so that we don't overwrite an existing object. + auto it = m->find(key); + if (it == m->end()) + new ((*m)[key].buf) Value(value.Pass()); + } + static inline KeyConstRefType GetKey( + const typename std::map<KeyStorageType, ValueStorageType>::const_iterator& + it) { + return it->first; + } + static inline ValueConstRefType GetValue( + const typename std::map<KeyStorageType, ValueStorageType>::const_iterator& + it) { + return *reinterpret_cast<const Value*>(it->second.buf); + } + static inline ValueRefType GetValue( + const typename std::map<KeyStorageType, ValueStorageType>::iterator& it) { + return *reinterpret_cast<Value*>(it->second.buf); + } + static inline void Clone( + const std::map<KeyStorageType, ValueStorageType>& src, + std::map<KeyStorageType, ValueStorageType>* dst) { + Finalize(dst); + dst->clear(); + for (auto it = src.begin(); it != src.end(); ++it) + new ((*dst)[it->first].buf) Value(GetValue(it).Clone()); + } +}; + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_MAP_INTERNAL_H_ diff --git a/mojo/public/cpp/bindings/lib/map_serialization.h b/mojo/public/cpp/bindings/lib/map_serialization.h new file mode 100644 index 0000000..6014b36 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/map_serialization.h @@ -0,0 +1,182 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_MAP_SERIALIZATION_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MAP_SERIALIZATION_H_ + +#include "mojo/public/cpp/bindings/lib/array_internal.h" +#include "mojo/public/cpp/bindings/lib/map_data_internal.h" +#include "mojo/public/cpp/bindings/lib/map_internal.h" +#include "mojo/public/cpp/bindings/lib/string_serialization.h" +#include "mojo/public/cpp/bindings/map.h" + +namespace mojo { + +template <typename Key, typename Value> +inline size_t GetSerializedSize_(const Map<Key, Value>& input); + +template <typename ValidateParams, typename E, typename F> +inline void SerializeArray_( + Array<E> input, + internal::Buffer* buf, + internal::Array_Data<F>** output, + const internal::ArrayValidateParams* validate_params); + +namespace internal { + +template <typename MapType, + typename DataType, + bool value_is_move_only_type = IsMoveOnlyType<MapType>::value, + bool is_union = + IsUnionDataType<typename RemovePointer<DataType>::type>::value> +struct MapSerializer; + +template <typename MapType, typename DataType> +struct MapSerializer<MapType, DataType, false, false> { + static size_t GetBaseArraySize(size_t count) { + return Align(count * sizeof(DataType)); + } + static size_t GetItemSize(const MapType& item) { return 0; } +}; + +template <> +struct MapSerializer<bool, bool, false, false> { + static size_t GetBaseArraySize(size_t count) { + return Align((count + 7) / 8); + } + static size_t GetItemSize(bool item) { return 0; } +}; + +template <typename H> +struct MapSerializer<ScopedHandleBase<H>, H, true, false> { + static size_t GetBaseArraySize(size_t count) { + return Align(count * sizeof(H)); + } + static size_t GetItemSize(const ScopedHandleBase<H>& item) { return 0; } +}; + +// This template must only apply to pointer mojo entity (structs and arrays). +// This is done by ensuring that WrapperTraits<S>::DataType is a pointer. +template <typename S> +struct MapSerializer< + S, + typename EnableIf<IsPointer<typename WrapperTraits<S>::DataType>::value, + typename WrapperTraits<S>::DataType>::type, + true, + false> { + typedef + typename RemovePointer<typename WrapperTraits<S>::DataType>::type S_Data; + static size_t GetBaseArraySize(size_t count) { + return count * sizeof(StructPointer<S_Data>); + } + static size_t GetItemSize(const S& item) { return GetSerializedSize_(item); } +}; + +template <typename U, typename U_Data> +struct MapSerializer<U, U_Data*, true, true> { + static size_t GetBaseArraySize(size_t count) { + return count * sizeof(U_Data); + } + static size_t GetItemSize(const U& item) { + return GetSerializedSize_(item, true); + } +}; + +template <> +struct MapSerializer<String, String_Data*, false, false> { + static size_t GetBaseArraySize(size_t count) { + return count * sizeof(StringPointer); + } + static size_t GetItemSize(const String& item) { + return GetSerializedSize_(item); + } +}; + +} // namespace internal + +// TODO(erg): This can't go away yet. We still need to calculate out the size +// of a struct header, and two arrays. +template <typename MapKey, typename MapValue> +inline size_t GetSerializedSize_(const Map<MapKey, MapValue>& input) { + if (!input) + return 0; + typedef typename internal::WrapperTraits<MapKey>::DataType DataKey; + typedef typename internal::WrapperTraits<MapValue>::DataType DataValue; + + size_t count = input.size(); + size_t struct_overhead = sizeof(mojo::internal::Map_Data<DataKey, DataValue>); + size_t key_base_size = + sizeof(internal::ArrayHeader) + + internal::MapSerializer<MapKey, DataKey>::GetBaseArraySize(count); + size_t value_base_size = + sizeof(internal::ArrayHeader) + + internal::MapSerializer<MapValue, DataValue>::GetBaseArraySize(count); + + size_t key_data_size = 0; + size_t value_data_size = 0; + for (auto it = input.begin(); it != input.end(); ++it) { + key_data_size += + internal::MapSerializer<MapKey, DataKey>::GetItemSize(it.GetKey()); + value_data_size += + internal::MapSerializer<MapValue, DataValue>::GetItemSize( + it.GetValue()); + } + + return struct_overhead + key_base_size + key_data_size + value_base_size + + value_data_size; +} + +// We don't need an ArrayValidateParams instance for key validation since +// we can deduce it from the Key type. (which can only be primitive types or +// non-nullable strings.) +template <typename MapKey, + typename MapValue, + typename DataKey, + typename DataValue> +inline void SerializeMap_( + Map<MapKey, MapValue> input, + internal::Buffer* buf, + internal::Map_Data<DataKey, DataValue>** output, + const internal::ArrayValidateParams* value_validate_params) { + if (input) { + internal::Map_Data<DataKey, DataValue>* result = + internal::Map_Data<DataKey, DataValue>::New(buf); + if (result) { + Array<MapKey> keys; + Array<MapValue> values; + input.DecomposeMapTo(&keys, &values); + const internal::ArrayValidateParams* key_validate_params = + internal::MapKeyValidateParamsFactory<DataKey>::Get(); + SerializeArray_(keys.Pass(), buf, &result->keys.ptr, key_validate_params); + SerializeArray_(values.Pass(), buf, &result->values.ptr, + value_validate_params); + } + *output = result; + } else { + *output = nullptr; + } +} + +template <typename MapKey, + typename MapValue, + typename DataKey, + typename DataValue> +inline void Deserialize_(internal::Map_Data<DataKey, DataValue>* input, + Map<MapKey, MapValue>* output) { + if (input) { + Array<MapKey> keys; + Array<MapValue> values; + + Deserialize_(input->keys.ptr, &keys); + Deserialize_(input->values.ptr, &values); + + *output = Map<MapKey, MapValue>(keys.Pass(), values.Pass()); + } else { + output->reset(); + } +} + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_MAP_SERIALIZATION_H_ diff --git a/mojo/public/cpp/bindings/lib/message.cc b/mojo/public/cpp/bindings/lib/message.cc new file mode 100644 index 0000000..6b563e7 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/message.cc @@ -0,0 +1,105 @@ +// 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. + +#include "mojo/public/cpp/bindings/message.h" + +#include <stdlib.h> + +#include <algorithm> + +#include "mojo/public/cpp/environment/logging.h" + +namespace mojo { + +Message::Message() { + Initialize(); +} + +Message::~Message() { + FreeDataAndCloseHandles(); +} + +void Message::Reset() { + FreeDataAndCloseHandles(); + + handles_.clear(); + Initialize(); +} + +void Message::AllocData(uint32_t num_bytes) { + MOJO_DCHECK(!data_); + data_num_bytes_ = num_bytes; + data_ = static_cast<internal::MessageData*>(calloc(num_bytes, 1)); +} + +void Message::AllocUninitializedData(uint32_t num_bytes) { + MOJO_DCHECK(!data_); + data_num_bytes_ = num_bytes; + data_ = static_cast<internal::MessageData*>(malloc(num_bytes)); +} + +void Message::MoveTo(Message* destination) { + MOJO_DCHECK(this != destination); + + destination->FreeDataAndCloseHandles(); + + // No copy needed. + destination->data_num_bytes_ = data_num_bytes_; + destination->data_ = data_; + std::swap(destination->handles_, handles_); + + handles_.clear(); + Initialize(); +} + +void Message::Initialize() { + data_num_bytes_ = 0; + data_ = nullptr; +} + +void Message::FreeDataAndCloseHandles() { + free(data_); + + for (std::vector<Handle>::iterator it = handles_.begin(); + it != handles_.end(); ++it) { + if (it->is_valid()) + CloseRaw(*it); + } +} + +MojoResult ReadAndDispatchMessage(MessagePipeHandle handle, + MessageReceiver* receiver, + bool* receiver_result) { + MojoResult rv; + + uint32_t num_bytes = 0, num_handles = 0; + rv = ReadMessageRaw(handle, + nullptr, + &num_bytes, + nullptr, + &num_handles, + MOJO_READ_MESSAGE_FLAG_NONE); + if (rv != MOJO_RESULT_RESOURCE_EXHAUSTED) + return rv; + + Message message; + message.AllocUninitializedData(num_bytes); + message.mutable_handles()->resize(num_handles); + + rv = ReadMessageRaw( + handle, + message.mutable_data(), + &num_bytes, + message.mutable_handles()->empty() + ? nullptr + : reinterpret_cast<MojoHandle*>(&message.mutable_handles()->front()), + &num_handles, + MOJO_READ_MESSAGE_FLAG_NONE); + if (receiver && rv == MOJO_RESULT_OK) + *receiver_result = receiver->Accept(&message); + + return rv; +} + +} // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/message_builder.cc b/mojo/public/cpp/bindings/lib/message_builder.cc new file mode 100644 index 0000000..25f1862 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/message_builder.cc @@ -0,0 +1,52 @@ +// 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. + +#include "mojo/public/cpp/bindings/lib/message_builder.h" + +#include "mojo/public/cpp/bindings/lib/bindings_serialization.h" +#include "mojo/public/cpp/bindings/message.h" + +namespace mojo { +namespace internal { + +template <typename Header> +void Allocate(Buffer* buf, Header** header) { + *header = static_cast<Header*>(buf->Allocate(sizeof(Header))); + (*header)->num_bytes = sizeof(Header); +} + +MessageBuilder::MessageBuilder(uint32_t name, size_t payload_size) { + Initialize(sizeof(MessageHeader) + payload_size); + + MessageHeader* header; + Allocate(&buf_, &header); + header->version = 0; + header->name = name; +} + +MessageBuilder::~MessageBuilder() { +} + +MessageBuilder::MessageBuilder() {} + +void MessageBuilder::Initialize(size_t size) { + message_.AllocData(static_cast<uint32_t>(Align(size))); + buf_.Initialize(message_.mutable_data(), message_.data_num_bytes()); +} + +MessageWithRequestIDBuilder::MessageWithRequestIDBuilder(uint32_t name, + size_t payload_size, + uint32_t flags, + uint64_t request_id) { + Initialize(sizeof(MessageHeaderWithRequestID) + payload_size); + MessageHeaderWithRequestID* header; + Allocate(&buf_, &header); + header->version = 1; + header->name = name; + header->flags = flags; + header->request_id = request_id; +} + +} // namespace internal +} // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/message_builder.h b/mojo/public/cpp/bindings/lib/message_builder.h new file mode 100644 index 0000000..86ae71d --- /dev/null +++ b/mojo/public/cpp/bindings/lib/message_builder.h @@ -0,0 +1,68 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_BUILDER_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_BUILDER_H_ + +#include <stdint.h> + +#include "mojo/public/cpp/bindings/lib/fixed_buffer.h" +#include "mojo/public/cpp/bindings/lib/message_internal.h" +#include "mojo/public/cpp/bindings/message.h" + +namespace mojo { +class Message; + +namespace internal { + +class MessageBuilder { + public: + MessageBuilder(uint32_t name, size_t payload_size); + ~MessageBuilder(); + + Buffer* buffer() { return &buf_; } + Message* message() { return &message_; } + + protected: + MessageBuilder(); + void Initialize(size_t size); + + Message message_; + FixedBuffer buf_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(MessageBuilder); +}; + +class MessageWithRequestIDBuilder : public MessageBuilder { + public: + MessageWithRequestIDBuilder(uint32_t name, + size_t payload_size, + uint32_t flags, + uint64_t request_id); +}; + +class RequestMessageBuilder : public MessageWithRequestIDBuilder { + public: + RequestMessageBuilder(uint32_t name, size_t payload_size) + : MessageWithRequestIDBuilder(name, + payload_size, + kMessageExpectsResponse, + 0) {} +}; + +class ResponseMessageBuilder : public MessageWithRequestIDBuilder { + public: + ResponseMessageBuilder(uint32_t name, + size_t payload_size, + uint64_t request_id) + : MessageWithRequestIDBuilder(name, + payload_size, + kMessageIsResponse, + request_id) {} +}; + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_BUILDER_H_ diff --git a/mojo/public/cpp/bindings/lib/message_filter.cc b/mojo/public/cpp/bindings/lib/message_filter.cc new file mode 100644 index 0000000..b09f40d --- /dev/null +++ b/mojo/public/cpp/bindings/lib/message_filter.cc @@ -0,0 +1,23 @@ +// 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. + +#include "mojo/public/cpp/bindings/message_filter.h" + +namespace mojo { + +MessageFilter::MessageFilter(MessageReceiver* sink) : sink_(sink) { +} + +MessageFilter::~MessageFilter() { +} + +PassThroughFilter::PassThroughFilter(MessageReceiver* sink) + : MessageFilter(sink) { +} + +bool PassThroughFilter::Accept(Message* message) { + return sink_->Accept(message); +} + +} // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/message_header_validator.cc b/mojo/public/cpp/bindings/lib/message_header_validator.cc new file mode 100644 index 0000000..940b15c --- /dev/null +++ b/mojo/public/cpp/bindings/lib/message_header_validator.cc @@ -0,0 +1,77 @@ +// 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. + +#include "mojo/public/cpp/bindings/lib/message_header_validator.h" + +#include "mojo/public/cpp/bindings/lib/bounds_checker.h" +#include "mojo/public/cpp/bindings/lib/validation_errors.h" +#include "mojo/public/cpp/bindings/lib/validation_util.h" + +namespace mojo { +namespace internal { +namespace { + +bool IsValidMessageHeader(const MessageHeader* header) { + // NOTE: Our goal is to preserve support for future extension of the message + // header. If we encounter fields we do not understand, we must ignore them. + + // Extra validation of the struct header: + if (header->version == 0) { + if (header->num_bytes != sizeof(MessageHeader)) { + ReportValidationError(VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER); + return false; + } + } else if (header->version == 1) { + if (header->num_bytes != sizeof(MessageHeaderWithRequestID)) { + ReportValidationError(VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER); + return false; + } + } else if (header->version > 1) { + if (header->num_bytes < sizeof(MessageHeaderWithRequestID)) { + ReportValidationError(VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER); + return false; + } + } + + // Validate flags (allow unknown bits): + + // These flags require a RequestID. + if (header->version < 1 && ((header->flags & kMessageExpectsResponse) || + (header->flags & kMessageIsResponse))) { + ReportValidationError(VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID); + return false; + } + + // These flags are mutually exclusive. + if ((header->flags & kMessageExpectsResponse) && + (header->flags & kMessageIsResponse)) { + ReportValidationError(VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS); + return false; + } + + return true; +} + +} // namespace + +MessageHeaderValidator::MessageHeaderValidator(MessageReceiver* sink) + : MessageFilter(sink) { +} + +bool MessageHeaderValidator::Accept(Message* message) { + // Pass 0 as number of handles because we don't expect any in the header, even + // if |message| contains handles. + BoundsChecker bounds_checker(message->data(), message->data_num_bytes(), 0); + + if (!ValidateStructHeaderAndClaimMemory(message->data(), &bounds_checker)) + return false; + + if (!IsValidMessageHeader(message->header())) + return false; + + return sink_->Accept(message); +} + +} // namespace internal +} // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/message_header_validator.h b/mojo/public/cpp/bindings/lib/message_header_validator.h new file mode 100644 index 0000000..bccef1f --- /dev/null +++ b/mojo/public/cpp/bindings/lib/message_header_validator.h @@ -0,0 +1,24 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_HEADER_VALIDATOR_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_HEADER_VALIDATOR_H_ + +#include "mojo/public/cpp/bindings/message.h" +#include "mojo/public/cpp/bindings/message_filter.h" + +namespace mojo { +namespace internal { + +class MessageHeaderValidator : public MessageFilter { + public: + explicit MessageHeaderValidator(MessageReceiver* sink = nullptr); + + bool Accept(Message* message) override; +}; + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_HEADER_VALIDATOR_H_ diff --git a/mojo/public/cpp/bindings/lib/message_internal.h b/mojo/public/cpp/bindings/lib/message_internal.h new file mode 100644 index 0000000..97b9619 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/message_internal.h @@ -0,0 +1,50 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_INTERNAL_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_INTERNAL_H_ + +#include "mojo/public/cpp/bindings/lib/bindings_internal.h" + +namespace mojo { +namespace internal { + +#pragma pack(push, 1) + +enum { kMessageExpectsResponse = 1 << 0, kMessageIsResponse = 1 << 1 }; + +struct MessageHeader : internal::StructHeader { + // Interface ID for identifying multiple interfaces running on the same + // message pipe. + uint32_t interface_id; + // Message name, which is scoped to the interface that the message belongs to. + uint32_t name; + // 0 or either of the enum values defined above. + uint32_t flags; + // Unused padding to make the struct size a multiple of 8 bytes. + uint32_t padding; +}; +static_assert(sizeof(MessageHeader) == 24, "Bad sizeof(MessageHeader)"); + +struct MessageHeaderWithRequestID : MessageHeader { + // Only used if either kMessageExpectsResponse or kMessageIsResponse is set in + // order to match responses with corresponding requests. + uint64_t request_id; +}; +static_assert(sizeof(MessageHeaderWithRequestID) == 32, + "Bad sizeof(MessageHeaderWithRequestID)"); + +struct MessageData { + MessageHeader header; +}; + +static_assert(sizeof(MessageData) == sizeof(MessageHeader), + "Bad sizeof(MessageData)"); + +#pragma pack(pop) + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_INTERNAL_H_ diff --git a/mojo/public/cpp/bindings/lib/no_interface.cc b/mojo/public/cpp/bindings/lib/no_interface.cc new file mode 100644 index 0000000..9e0945c --- /dev/null +++ b/mojo/public/cpp/bindings/lib/no_interface.cc @@ -0,0 +1,20 @@ +// 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. + +#include "mojo/public/cpp/bindings/no_interface.h" + +namespace mojo { + +const char* NoInterface::Name_ = "mojo::NoInterface"; + +bool NoInterfaceStub::Accept(Message* message) { + return false; +} + +bool NoInterfaceStub::AcceptWithResponder(Message* message, + MessageReceiver* responder) { + return false; +} + +} // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/router.cc b/mojo/public/cpp/bindings/lib/router.cc new file mode 100644 index 0000000..fff72b4 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/router.cc @@ -0,0 +1,158 @@ +// 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. + +#include "mojo/public/cpp/bindings/lib/router.h" + +#include "mojo/public/cpp/environment/logging.h" + +namespace mojo { +namespace internal { + +// ---------------------------------------------------------------------------- + +class ResponderThunk : public MessageReceiverWithStatus { + public: + explicit ResponderThunk(const SharedData<Router*>& router) + : router_(router), accept_was_invoked_(false) {} + ~ResponderThunk() override { + if (!accept_was_invoked_) { + // The Mojo application handled a message that was expecting a response + // but did not send a response. + Router* router = router_.value(); + if (router) { + // We raise an error to signal the calling application that an error + // condition occurred. Without this the calling application would have + // no way of knowing it should stop waiting for a response. + router->RaiseError(); + } + } + } + + // MessageReceiver implementation: + bool Accept(Message* message) override { + accept_was_invoked_ = true; + MOJO_DCHECK(message->has_flag(kMessageIsResponse)); + + bool result = false; + + Router* router = router_.value(); + if (router) + result = router->Accept(message); + + return result; + } + + // MessageReceiverWithStatus implementation: + bool IsValid() override { + Router* router = router_.value(); + return router && !router->encountered_error() && router->is_valid(); + } + + private: + SharedData<Router*> router_; + bool accept_was_invoked_; +}; + +// ---------------------------------------------------------------------------- + +Router::HandleIncomingMessageThunk::HandleIncomingMessageThunk(Router* router) + : router_(router) { +} + +Router::HandleIncomingMessageThunk::~HandleIncomingMessageThunk() { +} + +bool Router::HandleIncomingMessageThunk::Accept(Message* message) { + return router_->HandleIncomingMessage(message); +} + +// ---------------------------------------------------------------------------- + +Router::Router(ScopedMessagePipeHandle message_pipe, + FilterChain filters, + const MojoAsyncWaiter* waiter) + : thunk_(this), + filters_(filters.Pass()), + connector_(message_pipe.Pass(), waiter), + weak_self_(this), + incoming_receiver_(nullptr), + next_request_id_(0), + testing_mode_(false) { + filters_.SetSink(&thunk_); + connector_.set_incoming_receiver(filters_.GetHead()); +} + +Router::~Router() { + weak_self_.set_value(nullptr); + + for (auto& pair : responders_) + delete pair.second; +} + +bool Router::Accept(Message* message) { + MOJO_DCHECK(thread_checker_.CalledOnValidThread()); + MOJO_DCHECK(!message->has_flag(kMessageExpectsResponse)); + return connector_.Accept(message); +} + +bool Router::AcceptWithResponder(Message* message, MessageReceiver* responder) { + MOJO_DCHECK(thread_checker_.CalledOnValidThread()); + MOJO_DCHECK(message->has_flag(kMessageExpectsResponse)); + + // Reserve 0 in case we want it to convey special meaning in the future. + uint64_t request_id = next_request_id_++; + if (request_id == 0) + request_id = next_request_id_++; + + message->set_request_id(request_id); + if (!connector_.Accept(message)) + return false; + + // We assume ownership of |responder|. + responders_[request_id] = responder; + return true; +} + +void Router::EnableTestingMode() { + MOJO_DCHECK(thread_checker_.CalledOnValidThread()); + testing_mode_ = true; + connector_.set_enforce_errors_from_incoming_receiver(false); +} + +bool Router::HandleIncomingMessage(Message* message) { + MOJO_DCHECK(thread_checker_.CalledOnValidThread()); + if (message->has_flag(kMessageExpectsResponse)) { + if (!incoming_receiver_) + return false; + + MessageReceiverWithStatus* responder = new ResponderThunk(weak_self_); + bool ok = incoming_receiver_->AcceptWithResponder(message, responder); + if (!ok) + delete responder; + return ok; + + } else if (message->has_flag(kMessageIsResponse)) { + uint64_t request_id = message->request_id(); + ResponderMap::iterator it = responders_.find(request_id); + if (it == responders_.end()) { + MOJO_DCHECK(testing_mode_); + return false; + } + MessageReceiver* responder = it->second; + responders_.erase(it); + bool ok = responder->Accept(message); + delete responder; + return ok; + } else { + if (!incoming_receiver_) + return false; + + return incoming_receiver_->Accept(message); + } +} + +// ---------------------------------------------------------------------------- + +} // namespace internal +} // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/router.h b/mojo/public/cpp/bindings/lib/router.h new file mode 100644 index 0000000..1697610 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/router.h @@ -0,0 +1,141 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_ROUTER_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_ROUTER_H_ + +#include <map> + +#include "mojo/public/cpp/bindings/callback.h" +#include "mojo/public/cpp/bindings/lib/connector.h" +#include "mojo/public/cpp/bindings/lib/filter_chain.h" +#include "mojo/public/cpp/bindings/lib/shared_data.h" +#include "mojo/public/cpp/bindings/lib/thread_checker.h" +#include "mojo/public/cpp/environment/environment.h" +#include "mojo/public/cpp/environment/logging.h" + +namespace mojo { +namespace internal { + +class Router : public MessageReceiverWithResponder { + public: + Router(ScopedMessagePipeHandle message_pipe, + FilterChain filters, + const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()); + ~Router() override; + + // Sets the receiver to handle messages read from the message pipe that do + // not have the kMessageIsResponse flag set. + void set_incoming_receiver(MessageReceiverWithResponderStatus* receiver) { + incoming_receiver_ = receiver; + } + + // Sets the error handler to receive notifications when an error is + // encountered while reading from the pipe or waiting to read from the pipe. + void set_connection_error_handler(const Closure& error_handler) { + connector_.set_connection_error_handler(error_handler); + } + + // Returns true if an error was encountered while reading from the pipe or + // waiting to read from the pipe. + bool encountered_error() const { + MOJO_DCHECK(thread_checker_.CalledOnValidThread()); + return connector_.encountered_error(); + } + + // Is the router bound to a MessagePipe handle? + bool is_valid() const { + MOJO_DCHECK(thread_checker_.CalledOnValidThread()); + return connector_.is_valid(); + } + + // Please note that this method shouldn't be called unless it results from an + // explicit request of the user of bindings (e.g., the user sets an + // InterfacePtr to null or closes a Binding). + void CloseMessagePipe() { + MOJO_DCHECK(thread_checker_.CalledOnValidThread()); + connector_.CloseMessagePipe(); + } + + ScopedMessagePipeHandle PassMessagePipe() { + MOJO_DCHECK(thread_checker_.CalledOnValidThread()); + return connector_.PassMessagePipe(); + } + + void RaiseError() { + MOJO_DCHECK(thread_checker_.CalledOnValidThread()); + connector_.RaiseError(); + } + + // MessageReceiver implementation: + bool Accept(Message* message) override; + bool AcceptWithResponder(Message* message, + MessageReceiver* responder) override; + + // Blocks the current thread until the first incoming method call, i.e., + // either a call to a client method or a callback method, or |deadline|. + bool WaitForIncomingMessage(MojoDeadline deadline) { + MOJO_DCHECK(thread_checker_.CalledOnValidThread()); + return connector_.WaitForIncomingMessage(deadline); + } + + // See Binding for details of pause/resume. + void PauseIncomingMethodCallProcessing() { + MOJO_DCHECK(thread_checker_.CalledOnValidThread()); + connector_.PauseIncomingMethodCallProcessing(); + } + void ResumeIncomingMethodCallProcessing() { + MOJO_DCHECK(thread_checker_.CalledOnValidThread()); + connector_.ResumeIncomingMethodCallProcessing(); + } + + // Sets this object to testing mode. + // In testing mode: + // - the object is more tolerant of unrecognized response messages; + // - the connector continues working after seeing errors from its incoming + // receiver. + void EnableTestingMode(); + + MessagePipeHandle handle() const { return connector_.handle(); } + + // Returns true if this Router has any pending callbacks. + bool has_pending_responders() const { + MOJO_DCHECK(thread_checker_.CalledOnValidThread()); + return !responders_.empty(); + } + + private: + typedef std::map<uint64_t, MessageReceiver*> ResponderMap; + + class HandleIncomingMessageThunk : public MessageReceiver { + public: + HandleIncomingMessageThunk(Router* router); + ~HandleIncomingMessageThunk() override; + + // MessageReceiver implementation: + bool Accept(Message* message) override; + + private: + Router* router_; + }; + + bool HandleIncomingMessage(Message* message); + + HandleIncomingMessageThunk thunk_; + FilterChain filters_; + Connector connector_; + SharedData<Router*> weak_self_; + MessageReceiverWithResponderStatus* incoming_receiver_; + // Maps from the id of a response to the MessageReceiver that handles the + // response. + ResponderMap responders_; + uint64_t next_request_id_; + bool testing_mode_; + ThreadChecker thread_checker_; +}; + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_ROUTER_H_ diff --git a/mojo/public/cpp/bindings/lib/shared_data.h b/mojo/public/cpp/bindings/lib/shared_data.h new file mode 100644 index 0000000..2676224 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/shared_data.h @@ -0,0 +1,83 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_SHARED_DATA_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_SHARED_DATA_H_ + +#include <assert.h> + +#include "mojo/public/cpp/bindings/lib/thread_checker.h" +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { +namespace internal { + +// Used to allocate an instance of T that can be shared via reference counting. +template <typename T> +class SharedData { + public: + ~SharedData() { holder_->Release(); } + + SharedData() : holder_(new Holder()) {} + + explicit SharedData(const T& value) : holder_(new Holder(value)) {} + + SharedData(const SharedData<T>& other) : holder_(other.holder_) { + holder_->Retain(); + } + + SharedData<T>& operator=(const SharedData<T>& other) { + if (other.holder_ == holder_) + return *this; + holder_->Release(); + holder_ = other.holder_; + holder_->Retain(); + return *this; + } + + void reset() { + holder_->Release(); + holder_ = new Holder(); + } + + void reset(const T& value) { + holder_->Release(); + holder_ = new Holder(value); + } + + void set_value(const T& value) { holder_->value = value; } + T* mutable_value() { return &holder_->value; } + const T& value() const { return holder_->value; } + + private: + class Holder { + public: + Holder() : value(), ref_count_(1) {} + Holder(const T& value) : value(value), ref_count_(1) {} + + void Retain() { + assert(thread_checker_.CalledOnValidThread()); + ++ref_count_; + } + void Release() { + assert(thread_checker_.CalledOnValidThread()); + if (--ref_count_ == 0) + delete this; + } + + T value; + + private: + int ref_count_; + ThreadChecker thread_checker_; + MOJO_DISALLOW_COPY_AND_ASSIGN(Holder); + }; + + Holder* holder_; +}; + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_SHARED_DATA_H_ diff --git a/mojo/public/cpp/bindings/lib/shared_ptr.h b/mojo/public/cpp/bindings/lib/shared_ptr.h new file mode 100644 index 0000000..37c8735 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/shared_ptr.h @@ -0,0 +1,57 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_SHARED_PTR_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_SHARED_PTR_H_ + +#include "mojo/public/cpp/bindings/lib/shared_data.h" + +namespace mojo { +namespace internal { + +// Used to manage a heap-allocated instance of P that can be shared via +// reference counting. When the last reference is dropped, the instance is +// deleted. +template <typename P> +class SharedPtr { + public: + SharedPtr() {} + + explicit SharedPtr(P* ptr) { impl_.mutable_value()->ptr = ptr; } + + // Default copy-constructor and assignment operator are OK. + + P* get() { return impl_.value().ptr; } + const P* get() const { return impl_.value().ptr; } + + void reset() { impl_.reset(); } + + P* operator->() { return get(); } + const P* operator->() const { return get(); } + + private: + class Impl { + public: + ~Impl() { + if (ptr) + delete ptr; + } + + Impl() : ptr(nullptr) {} + + Impl(P* ptr) : ptr(ptr) {} + + P* ptr; + + private: + MOJO_DISALLOW_COPY_AND_ASSIGN(Impl); + }; + + SharedData<Impl> impl_; +}; + +} // namespace mojo +} // namespace internal + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_SHARED_PTR_H_ diff --git a/mojo/public/cpp/bindings/lib/string_serialization.cc b/mojo/public/cpp/bindings/lib/string_serialization.cc new file mode 100644 index 0000000..e29d6f8 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/string_serialization.cc @@ -0,0 +1,40 @@ +// 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. + +#include "mojo/public/cpp/bindings/lib/string_serialization.h" + +#include <string.h> + +namespace mojo { + +size_t GetSerializedSize_(const String& input) { + if (!input) + return 0; + return internal::Align(sizeof(internal::String_Data) + input.size()); +} + +void Serialize_(const String& input, + internal::Buffer* buf, + internal::String_Data** output) { + if (input) { + internal::String_Data* result = + internal::String_Data::New(input.size(), buf); + if (result) + memcpy(result->storage(), input.data(), input.size()); + *output = result; + } else { + *output = nullptr; + } +} + +void Deserialize_(internal::String_Data* input, String* output) { + if (input) { + String result(input->storage(), input->size()); + result.Swap(output); + } else { + output->reset(); + } +} + +} // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/string_serialization.h b/mojo/public/cpp/bindings/lib/string_serialization.h new file mode 100644 index 0000000..0118742 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/string_serialization.h @@ -0,0 +1,21 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_STRING_SERIALIZATION_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_STRING_SERIALIZATION_H_ + +#include "mojo/public/cpp/bindings/lib/array_internal.h" +#include "mojo/public/cpp/bindings/string.h" + +namespace mojo { + +size_t GetSerializedSize_(const String& input); +void Serialize_(const String& input, + internal::Buffer* buffer, + internal::String_Data** output); +void Deserialize_(internal::String_Data* input, String* output); + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_STRING_SERIALIZATION_H_ diff --git a/mojo/public/cpp/bindings/lib/template_util.h b/mojo/public/cpp/bindings/lib/template_util.h new file mode 100644 index 0000000..9a5788c --- /dev/null +++ b/mojo/public/cpp/bindings/lib/template_util.h @@ -0,0 +1,130 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_TEMPLATE_UTIL_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_TEMPLATE_UTIL_H_ + +namespace mojo { +namespace internal { + +template <class T, T v> +struct IntegralConstant { + static const T value = v; +}; + +template <class T, T v> +const T IntegralConstant<T, v>::value; + +typedef IntegralConstant<bool, true> TrueType; +typedef IntegralConstant<bool, false> FalseType; + +template <class T> +struct IsConst : FalseType {}; +template <class T> +struct IsConst<const T> : TrueType {}; + +template <class T> +struct IsPointer : FalseType {}; +template <class T> +struct IsPointer<T*> : TrueType {}; + +template <bool B, typename T = void> +struct EnableIf {}; + +template <typename T> +struct EnableIf<true, T> { + typedef T type; +}; + +// Types YesType and NoType are guaranteed such that sizeof(YesType) < +// sizeof(NoType). +typedef char YesType; + +struct NoType { + YesType dummy[2]; +}; + +// A helper template to determine if given type is non-const move-only-type, +// i.e. if a value of the given type should be passed via .Pass() in a +// destructive way. +template <typename T> +struct IsMoveOnlyType { + template <typename U> + static YesType Test(const typename U::MoveOnlyTypeForCPP03*); + + template <typename U> + static NoType Test(...); + + static const bool value = + sizeof(Test<T>(0)) == sizeof(YesType) && !IsConst<T>::value; +}; + +// Returns a reference to |t| when T is not a move-only type. +template <typename T> +typename EnableIf<!IsMoveOnlyType<T>::value, T>::type& Forward(T& t) { + return t; +} + +// Returns the result of t.Pass() when T is a move-only type. +template <typename T> +typename EnableIf<IsMoveOnlyType<T>::value, T>::type Forward(T& t) { + return t.Pass(); +} + +// This goop is a trick used to implement a template that can be used to +// determine if a given class is the base class of another given class. +template <typename, typename> +struct IsSame { + static bool const value = false; +}; +template <typename A> +struct IsSame<A, A> { + static bool const value = true; +}; +template <typename Base, typename Derived> +struct IsBaseOf { + private: + // This class doesn't work correctly with forward declarations. + // Because sizeof cannot be applied to incomplete types, this line prevents us + // from passing in forward declarations. + typedef char (*EnsureTypesAreComplete)[sizeof(Base) + sizeof(Derived)]; + + static Derived* CreateDerived(); + static char(&Check(Base*))[1]; + static char(&Check(...))[2]; + + public: + static bool const value = sizeof Check(CreateDerived()) == 1 && + !IsSame<Base const, void const>::value; +}; + +template <class T> +struct RemovePointer { + typedef T type; +}; +template <class T> +struct RemovePointer<T*> { + typedef T type; +}; + +template <template <typename...> class Template, typename T> +struct IsSpecializationOf : FalseType {}; + +template <template <typename...> class Template, typename... Args> +struct IsSpecializationOf<Template, Template<Args...>> : TrueType {}; + +template <bool B, typename T, typename F> +struct Conditional { + typedef T type; +}; + +template <typename T, typename F> +struct Conditional<false, T, F> { + typedef F type; +}; + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_TEMPLATE_UTIL_H_ diff --git a/mojo/public/cpp/bindings/lib/thread_checker.h b/mojo/public/cpp/bindings/lib/thread_checker.h new file mode 100644 index 0000000..da45d0a --- /dev/null +++ b/mojo/public/cpp/bindings/lib/thread_checker.h @@ -0,0 +1,37 @@ +// Copyright 2015 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_LIB_THREAD_CHECKER_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_THREAD_CHECKER_H_ + +#include "mojo/public/cpp/system/macros.h" + +#if !defined(_WIN32) +#include "mojo/public/cpp/bindings/lib/thread_checker_posix.h" +#endif + +namespace mojo { +namespace internal { + +class ThreadCheckerDoNothing { + public: + bool CalledOnValidThread() const MOJO_WARN_UNUSED_RESULT { + return true; + } +}; + +// ThreadChecker is a class used to verify that some methods of a class are +// called from the same thread. It is meant to be a member variable of a class. +// The entire lifecycle of a ThreadChecker must occur on a single thread. +// In Release mode (without dcheck_always_on), ThreadChecker does nothing. +#if !defined(_WIN32) && (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) +using ThreadChecker = ThreadCheckerPosix; +#else +using ThreadChecker = ThreadCheckerDoNothing; +#endif + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_THREAD_CHECKER_H_ diff --git a/mojo/public/cpp/bindings/lib/thread_checker_posix.cc b/mojo/public/cpp/bindings/lib/thread_checker_posix.cc new file mode 100644 index 0000000..c8a16e2 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/thread_checker_posix.cc @@ -0,0 +1,24 @@ +// Copyright 2015 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. + +#include "mojo/public/cpp/bindings/lib/thread_checker_posix.h" + +#include <assert.h> + +namespace mojo { +namespace internal { + +ThreadCheckerPosix::ThreadCheckerPosix() : attached_thread_id_(pthread_self()) { +} + +ThreadCheckerPosix::~ThreadCheckerPosix() { + assert(CalledOnValidThread()); +} + +bool ThreadCheckerPosix::CalledOnValidThread() const { + return pthread_equal(pthread_self(), attached_thread_id_); +} + +} // namespace internal +} // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/thread_checker_posix.h b/mojo/public/cpp/bindings/lib/thread_checker_posix.h new file mode 100644 index 0000000..4701b88 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/thread_checker_posix.h @@ -0,0 +1,31 @@ +// Copyright 2015 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_LIB_THREAD_CHECKER_POSIX_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_THREAD_CHECKER_POSIX_H_ + +#include <pthread.h> + +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { +namespace internal { + +// An implementation of ThreadChecker for POSIX systems. +class ThreadCheckerPosix { + public: + ThreadCheckerPosix(); + ~ThreadCheckerPosix(); + + bool CalledOnValidThread() const MOJO_WARN_UNUSED_RESULT; + private: + const pthread_t attached_thread_id_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(ThreadCheckerPosix); +}; + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_THREAD_CHECKER_POSIX_H_ diff --git a/mojo/public/cpp/bindings/lib/union_accessor.h b/mojo/public/cpp/bindings/lib/union_accessor.h new file mode 100644 index 0000000..821aede --- /dev/null +++ b/mojo/public/cpp/bindings/lib/union_accessor.h @@ -0,0 +1,33 @@ +// Copyright 2015 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_LIB_UNION_ACCESSOR_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_UNION_ACCESSOR_H_ + +namespace mojo { +namespace internal { + +// When serializing and deserializing Unions, it is necessary to access +// the private fields and methods of the Union. This allows us to do that +// without leaking those same fields and methods in the Union interface. +// All Union wrappers are friends of this class allowing such access. +template <typename U> +class UnionAccessor { + public: + explicit UnionAccessor(U* u) : u_(u) {} + + typename U::Union_* data() { return &(u_->data_); } + + typename U::Tag* tag() { return &(u_->tag_); } + + void SwitchActive(typename U::Tag new_tag) { u_->SwitchActive(new_tag); } + + private: + U* u_; +}; + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_UNION_ACCESSOR_H_ diff --git a/mojo/public/cpp/bindings/lib/validate_params.h b/mojo/public/cpp/bindings/lib/validate_params.h new file mode 100644 index 0000000..274a012 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/validate_params.h @@ -0,0 +1,49 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_VALIDATE_PARAMS_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_VALIDATE_PARAMS_H_ + +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { +namespace internal { + +class ArrayValidateParams { + public: + // ArrayValidateParams takes ownership of |in_element_validate params|. + ArrayValidateParams(uint32_t in_expected_num_elements, + bool in_element_is_nullable, + ArrayValidateParams* in_element_validate_params) + : expected_num_elements(in_expected_num_elements), + element_is_nullable(in_element_is_nullable), + element_validate_params(in_element_validate_params) {} + + ~ArrayValidateParams() { + if (element_validate_params) + delete element_validate_params; + } + + // TODO(vtl): The members of this class shouldn't be public. + + // If |expected_num_elements| is not 0, the array is expected to have exactly + // that number of elements. + uint32_t expected_num_elements; + + // Whether the elements are nullable. + bool element_is_nullable; + + // Validation information for elements. It is either a pointer to another + // instance of ArrayValidateParams (if elements are arrays or maps), or + // nullptr. In the case of maps, this is used to validate the value array. + ArrayValidateParams* element_validate_params; + + private: + MOJO_DISALLOW_COPY_AND_ASSIGN(ArrayValidateParams); +}; + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_VALIDATE_PARAMS_H_ diff --git a/mojo/public/cpp/bindings/lib/validation_errors.cc b/mojo/public/cpp/bindings/lib/validation_errors.cc new file mode 100644 index 0000000..ee58ed9 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/validation_errors.cc @@ -0,0 +1,98 @@ +// 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. + +#include "mojo/public/cpp/bindings/lib/validation_errors.h" + +#include "mojo/public/cpp/environment/logging.h" + +namespace mojo { +namespace internal { +namespace { + +ValidationErrorObserverForTesting* g_validation_error_observer = nullptr; +SerializationWarningObserverForTesting* g_serialization_warning_observer = + nullptr; + +} // namespace + +const char* ValidationErrorToString(ValidationError error) { + switch (error) { + case VALIDATION_ERROR_NONE: + return "VALIDATION_ERROR_NONE"; + case VALIDATION_ERROR_MISALIGNED_OBJECT: + return "VALIDATION_ERROR_MISALIGNED_OBJECT"; + case VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE: + return "VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE"; + case VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER: + return "VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER"; + case VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER: + return "VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER"; + case VALIDATION_ERROR_ILLEGAL_HANDLE: + return "VALIDATION_ERROR_ILLEGAL_HANDLE"; + case VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE: + return "VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE"; + case VALIDATION_ERROR_ILLEGAL_POINTER: + return "VALIDATION_ERROR_ILLEGAL_POINTER"; + case VALIDATION_ERROR_UNEXPECTED_NULL_POINTER: + return "VALIDATION_ERROR_UNEXPECTED_NULL_POINTER"; + case VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS: + return "VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS"; + case VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID: + return "VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID"; + case VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD: + return "VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD"; + case VALIDATION_ERROR_DIFFERENT_SIZED_ARRAYS_IN_MAP: + return "VALIDATION_ERROR_DIFFERENT_SIZED_ARRAYS_IN_MAP"; + case VALIDATION_ERROR_UNKNOWN_UNION_TAG: + return "VALIDATION_ERROR_UNKNOWN_UNION_TAG"; + } + + return "Unknown error"; +} + +void ReportValidationError(ValidationError error, const char* description) { + if (g_validation_error_observer) { + g_validation_error_observer->set_last_error(error); + } else if (description) { + MOJO_LOG(ERROR) << "Invalid message: " << ValidationErrorToString(error) + << " (" << description << ")"; + } else { + MOJO_LOG(ERROR) << "Invalid message: " << ValidationErrorToString(error); + } +} + +ValidationErrorObserverForTesting::ValidationErrorObserverForTesting() + : last_error_(VALIDATION_ERROR_NONE) { + MOJO_DCHECK(!g_validation_error_observer); + g_validation_error_observer = this; +} + +ValidationErrorObserverForTesting::~ValidationErrorObserverForTesting() { + MOJO_DCHECK(g_validation_error_observer == this); + g_validation_error_observer = nullptr; +} + +bool ReportSerializationWarning(ValidationError error) { + if (g_serialization_warning_observer) { + g_serialization_warning_observer->set_last_warning(error); + return true; + } + + return false; +} + +SerializationWarningObserverForTesting::SerializationWarningObserverForTesting() + : last_warning_(VALIDATION_ERROR_NONE) { + MOJO_DCHECK(!g_serialization_warning_observer); + g_serialization_warning_observer = this; +} + +SerializationWarningObserverForTesting:: + ~SerializationWarningObserverForTesting() { + MOJO_DCHECK(g_serialization_warning_observer == this); + g_serialization_warning_observer = nullptr; +} + +} // namespace internal +} // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/validation_errors.h b/mojo/public/cpp/bindings/lib/validation_errors.h new file mode 100644 index 0000000..1d15bc9 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/validation_errors.h @@ -0,0 +1,122 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_VALIDATION_ERRORS_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_VALIDATION_ERRORS_H_ + +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { +namespace internal { + +enum ValidationError { + // There is no validation error. + VALIDATION_ERROR_NONE, + // An object (struct or array) is not 8-byte aligned. + VALIDATION_ERROR_MISALIGNED_OBJECT, + // An object is not contained inside the message data, or it overlaps other + // objects. + VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE, + // A struct header doesn't make sense, for example: + // - |num_bytes| is smaller than the size of the struct header. + // - |num_bytes| and |version| don't match. + // TODO(yzshen): Consider splitting it into two different error codes. Because + // the former indicates someone is misbehaving badly whereas the latter could + // be due to an inappropriately-modified .mojom file. + VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER, + // An array header doesn't make sense, for example: + // - |num_bytes| is smaller than the size of the header plus the size required + // to store |num_elements| elements. + // - For fixed-size arrays, |num_elements| is different than the specified + // size. + VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER, + // An encoded handle is illegal. + VALIDATION_ERROR_ILLEGAL_HANDLE, + // A non-nullable handle field is set to invalid handle. + VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE, + // An encoded pointer is illegal. + VALIDATION_ERROR_ILLEGAL_POINTER, + // A non-nullable pointer field is set to null. + VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, + // |flags| in the message header is invalid. The flags are either + // inconsistent with one another, inconsistent with other parts of the + // message, or unexpected for the message receiver. For example the + // receiver is expecting a request message but the flags indicate that + // the message is a response message. + VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS, + // |flags| in the message header indicates that a request ID is required but + // there isn't one. + VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID, + // The |name| field in a message header contains an unexpected value. + VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD, + // Two parallel arrays which are supposed to represent a map have different + // lengths. + VALIDATION_ERROR_DIFFERENT_SIZED_ARRAYS_IN_MAP, + // Attempted to deserialize a tagged union with an unknown tag. + VALIDATION_ERROR_UNKNOWN_UNION_TAG +}; + +const char* ValidationErrorToString(ValidationError error); + +void ReportValidationError(ValidationError error, + const char* description = nullptr); + +// Only used by validation tests and when there is only one thread doing message +// validation. +class ValidationErrorObserverForTesting { + public: + ValidationErrorObserverForTesting(); + ~ValidationErrorObserverForTesting(); + + ValidationError last_error() const { return last_error_; } + void set_last_error(ValidationError error) { last_error_ = error; } + + private: + ValidationError last_error_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(ValidationErrorObserverForTesting); +}; + +// Used only by MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING. Don't use it directly. +// +// The function returns true if the error is recorded (by a +// SerializationWarningObserverForTesting object), false otherwise. +bool ReportSerializationWarning(ValidationError error); + +// Only used by serialization tests and when there is only one thread doing +// message serialization. +class SerializationWarningObserverForTesting { + public: + SerializationWarningObserverForTesting(); + ~SerializationWarningObserverForTesting(); + + ValidationError last_warning() const { return last_warning_; } + void set_last_warning(ValidationError error) { last_warning_ = error; } + + private: + ValidationError last_warning_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(SerializationWarningObserverForTesting); +}; + +} // namespace internal +} // namespace mojo + +// In debug build, logs a serialization warning if |condition| evaluates to +// true: +// - if there is a SerializationWarningObserverForTesting object alive, +// records |error| in it; +// - otherwise, logs a fatal-level message. +// |error| is the validation error that will be triggered by the receiver +// of the serialzation result. +// +// In non-debug build, does nothing (not even compiling |condition|). +#define MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( \ + condition, error, description) \ + MOJO_DLOG_IF(FATAL, (condition) && !ReportSerializationWarning(error)) \ + << "The outgoing message will trigger " \ + << ValidationErrorToString(error) << " at the receiving side (" \ + << description << ")."; + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_VALIDATION_ERRORS_H_ diff --git a/mojo/public/cpp/bindings/lib/validation_util.cc b/mojo/public/cpp/bindings/lib/validation_util.cc new file mode 100644 index 0000000..2268a15 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/validation_util.cc @@ -0,0 +1,103 @@ +// Copyright 2015 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. + +#include "mojo/public/cpp/bindings/lib/validation_util.h" + +#include <limits> + +#include "mojo/public/cpp/bindings/lib/bindings_serialization.h" +#include "mojo/public/cpp/bindings/lib/message_internal.h" +#include "mojo/public/cpp/bindings/lib/validation_errors.h" +#include "mojo/public/interfaces/bindings/interface_control_messages.mojom.h" + +namespace mojo { +namespace internal { + +bool ValidateEncodedPointer(const uint64_t* offset) { + // - Make sure |*offset| is no more than 32-bits. + // - Cast |offset| to uintptr_t so overflow behavior is well defined across + // 32-bit and 64-bit systems. + return *offset <= std::numeric_limits<uint32_t>::max() && + (reinterpret_cast<uintptr_t>(offset) + + static_cast<uint32_t>(*offset) >= + reinterpret_cast<uintptr_t>(offset)); +} + +bool ValidateStructHeaderAndClaimMemory(const void* data, + BoundsChecker* bounds_checker) { + if (!IsAligned(data)) { + ReportValidationError(VALIDATION_ERROR_MISALIGNED_OBJECT); + return false; + } + if (!bounds_checker->IsValidRange(data, sizeof(StructHeader))) { + ReportValidationError(VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE); + return false; + } + + const StructHeader* header = static_cast<const StructHeader*>(data); + + if (header->num_bytes < sizeof(StructHeader)) { + ReportValidationError(VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER); + return false; + } + + if (!bounds_checker->ClaimMemory(data, header->num_bytes)) { + ReportValidationError(VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE); + return false; + } + + return true; +} + +bool ValidateMessageIsRequestWithoutResponse(const Message* message) { + if (message->has_flag(kMessageIsResponse) || + message->has_flag(kMessageExpectsResponse)) { + ReportValidationError(VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS); + return false; + } + return true; +} + +bool ValidateMessageIsRequestExpectingResponse(const Message* message) { + if (message->has_flag(kMessageIsResponse) || + !message->has_flag(kMessageExpectsResponse)) { + ReportValidationError(VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS); + return false; + } + return true; +} + +bool ValidateMessageIsResponse(const Message* message) { + if (message->has_flag(kMessageExpectsResponse) || + !message->has_flag(kMessageIsResponse)) { + ReportValidationError(VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS); + return false; + } + return true; +} + +bool ValidateControlRequest(const Message* message) { + switch (message->header()->name) { + case kRunMessageId: + return ValidateMessageIsRequestExpectingResponse(message) && + ValidateMessagePayload<RunMessageParams_Data>(message); + case kRunOrClosePipeMessageId: + return ValidateMessageIsRequestWithoutResponse(message) && + ValidateMessagePayload<RunOrClosePipeMessageParams_Data>(message); + } + return false; +} + +bool ValidateControlResponse(const Message* message) { + if (!ValidateMessageIsResponse(message)) + return false; + switch (message->header()->name) { + case kRunMessageId: + return ValidateMessagePayload<RunResponseMessageParams_Data>(message); + } + return false; +} + +} // namespace internal +} // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/validation_util.h b/mojo/public/cpp/bindings/lib/validation_util.h new file mode 100644 index 0000000..6853f45 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/validation_util.h @@ -0,0 +1,53 @@ +// Copyright 2015 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_LIB_VALIDATION_UTIL_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_VALIDATION_UTIL_H_ + +#include <stdint.h> + +#include "mojo/public/cpp/bindings/lib/bounds_checker.h" +#include "mojo/public/cpp/bindings/message.h" + +namespace mojo { +namespace internal { + +// Checks whether decoding the pointer will overflow and produce a pointer +// smaller than |offset|. +bool ValidateEncodedPointer(const uint64_t* offset); + +// Validates that |data| contains a valid struct header, in terms of alignment +// and size (i.e., the |num_bytes| field of the header is sufficient for storing +// the header itself). Besides, it checks that the memory range +// [data, data + num_bytes) is not marked as occupied by other objects in +// |bounds_checker|. On success, the memory range is marked as occupied. +// Note: Does not verify |version| or that |num_bytes| is correct for the +// claimed version. +bool ValidateStructHeaderAndClaimMemory(const void* data, + BoundsChecker* bounds_checker); + +// Validates that the message is a request which doesn't expect a response. +bool ValidateMessageIsRequestWithoutResponse(const Message* message); +// Validates that the message is a request expecting a response. +bool ValidateMessageIsRequestExpectingResponse(const Message* message); +// Validates that the message is a response. +bool ValidateMessageIsResponse(const Message* message); + +// Validates that the message payload is a valid struct of type ParamsType. +template <typename ParamsType> +bool ValidateMessagePayload(const Message* message) { + BoundsChecker bounds_checker(message->payload(), message->payload_num_bytes(), + message->handles()->size()); + return ParamsType::Validate(message->payload(), &bounds_checker); +} + +// The following methods validate control messages defined in +// interface_control_messages.mojom. +bool ValidateControlRequest(const Message* message); +bool ValidateControlResponse(const Message* message); + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_VALIDATION_UTIL_H_ diff --git a/mojo/public/cpp/bindings/lib/value_traits.h b/mojo/public/cpp/bindings/lib/value_traits.h new file mode 100644 index 0000000..d3b295b --- /dev/null +++ b/mojo/public/cpp/bindings/lib/value_traits.h @@ -0,0 +1,108 @@ +// Copyright 2015 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_LIB_VALUE_TRAITS_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_VALUE_TRAITS_H_ + +#include "mojo/public/cpp/bindings/lib/template_util.h" + +namespace mojo { + +template <typename T> +class Array; + +template <typename T> +class AssociatedInterfacePtrInfo; + +template <typename T> +class AssociatedInterfaceRequest; + +template <typename T> +class InlinedStructPtr; + +template <typename T> +class InterfacePtr; + +template <typename T> +class InterfaceRequest; + +template <typename K, typename V> +class Map; + +template <typename T> +class ScopedHandleBase; + +template <typename T> +class StructPtr; + +namespace internal { + +template <typename T, typename Enable = void> +struct ValueTraits { + static bool Equals(const T& a, const T& b) { return a == b; } +}; + +template <typename T> +struct ValueTraits< + T, + typename EnableIf<IsSpecializationOf<Array, T>::value || + IsSpecializationOf<Map, T>::value || + IsSpecializationOf<StructPtr, T>::value || + IsSpecializationOf<InlinedStructPtr, T>::value>::type> { + static bool Equals(const T& a, const T& b) { return a.Equals(b); } +}; + +template <typename T> +struct ValueTraits<ScopedHandleBase<T>> { + static bool Equals(const ScopedHandleBase<T>& a, + const ScopedHandleBase<T>& b) { + return a.get().value() == b.get().value(); + } +}; + +template <typename T> +struct ValueTraits< + T, + typename EnableIf< + IsSpecializationOf<InterfaceRequest, T>::value || + IsSpecializationOf<AssociatedInterfaceRequest, T>::value>::type> { + static bool Equals(const T& a, const T& b) { + if (&a == &b) + return true; + + // Now that |a| and |b| refer to different objects, they are equivalent if + // and only if they are both invalid. + return !a.is_pending() && !b.is_pending(); + } +}; + +template <typename T> +struct ValueTraits<InterfacePtr<T>> { + static bool Equals(const InterfacePtr<T>& a, const InterfacePtr<T>& b) { + if (&a == &b) + return true; + + // Now that |a| and |b| refer to different objects, they are equivalent if + // and only if they are both null. + return !a && !b; + } +}; + +template <typename T> +struct ValueTraits<AssociatedInterfacePtrInfo<T>> { + static bool Equals(const AssociatedInterfacePtrInfo<T>& a, + const AssociatedInterfacePtrInfo<T>& b) { + if (&a == &b) + return true; + + // Now that |a| and |b| refer to different objects, they are equivalent if + // and only if they are both invalid. + return !a.is_valid() && !b.is_valid(); + } +}; + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_VALUE_TRAITS_H_ diff --git a/mojo/public/cpp/bindings/map.h b/mojo/public/cpp/bindings/map.h new file mode 100644 index 0000000..0f08d83 --- /dev/null +++ b/mojo/public/cpp/bindings/map.h @@ -0,0 +1,296 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_MAP_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_MAP_H_ + +#include <map> + +#include "mojo/public/cpp/bindings/lib/map_internal.h" +#include "mojo/public/cpp/bindings/lib/value_traits.h" + +namespace mojo { + +// A move-only map that can handle move-only values. Map has the following +// characteristics: +// - The map itself can be null, and this is distinct from empty. +// - Keys must not be move-only. +// - The Key-type's "<" operator is used to sort the entries, and also is +// used to determine equality of the key values. +// - There can only be one entry per unique key. +// - Values of move-only types will be moved into the Map when they are added +// using the insert() method. +template <typename Key, typename Value> +class Map { + MOJO_MOVE_ONLY_TYPE(Map) + + public: + // Map keys cannot be move only classes. + static_assert(!internal::IsMoveOnlyType<Key>::value, + "Map keys cannot be move only types."); + + typedef internal::MapTraits<Key, + Value, + internal::IsMoveOnlyType<Value>::value> Traits; + typedef typename Traits::KeyStorageType KeyStorageType; + typedef typename Traits::KeyRefType KeyRefType; + typedef typename Traits::KeyConstRefType KeyConstRefType; + typedef typename Traits::KeyForwardType KeyForwardType; + + typedef typename Traits::ValueStorageType ValueStorageType; + typedef typename Traits::ValueRefType ValueRefType; + typedef typename Traits::ValueConstRefType ValueConstRefType; + typedef typename Traits::ValueForwardType ValueForwardType; + + typedef internal::Map_Data<typename internal::WrapperTraits<Key>::DataType, + typename internal::WrapperTraits<Value>::DataType> + Data_; + + Map() : is_null_(true) {} + + // Constructs a non-null Map containing the specified |keys| mapped to the + // corresponding |values|. + Map(mojo::Array<Key> keys, mojo::Array<Value> values) : is_null_(false) { + MOJO_DCHECK(keys.size() == values.size()); + Traits::InitializeFrom(&map_, keys.Pass(), values.Pass()); + } + + ~Map() { Traits::Finalize(&map_); } + + Map(Map&& other) : is_null_(true) { Take(&other); } + Map& operator=(Map&& other) { + Take(&other); + return *this; + } + + // Copies the contents of some other type of map into a new Map using a + // TypeConverter. A TypeConverter for std::map to Map is defined below. + template <typename U> + static Map From(const U& other) { + return TypeConverter<Map, U>::Convert(other); + } + + // Copies the contents of the Map into some other type of map. A TypeConverter + // for Map to std::map is defined below. + template <typename U> + U To() const { + return TypeConverter<U, Map>::Convert(*this); + } + + // Destroys the contents of the Map and leaves it in the null state. + void reset() { + if (!map_.empty()) { + Traits::Finalize(&map_); + map_.clear(); + } + is_null_ = true; + } + + bool is_null() const { return is_null_; } + + // Indicates the number of keys in the map. + size_t size() const { return map_.size(); } + + void mark_non_null() { is_null_ = false; } + + // Inserts a key-value pair into the map, moving the value by calling its + // Pass() method if it is a move-only type. Like std::map, this does not + // insert |value| if |key| is already a member of the map. + void insert(KeyForwardType key, ValueForwardType value) { + is_null_ = false; + Traits::Insert(&map_, key, value); + } + + // Returns a reference to the value associated with the specified key, + // crashing the process if the key is not present in the map. + ValueRefType at(KeyForwardType key) { return Traits::at(&map_, key); } + ValueConstRefType at(KeyForwardType key) const { + return Traits::at(&map_, key); + } + + // Returns a reference to the value associated with the specified key, + // creating a new entry if the key is not already present in the map. A + // newly-created value will be value-initialized (meaning that it will be + // initialized by the default constructor of the value type, if any, or else + // will be zero-initialized). + ValueRefType operator[](KeyForwardType key) { + is_null_ = false; + return Traits::GetOrInsert(&map_, key); + } + + // Swaps the contents of this Map with another Map of the same type (including + // nullness). + void Swap(Map<Key, Value>* other) { + std::swap(is_null_, other->is_null_); + map_.swap(other->map_); + } + + // Swaps the contents of this Map with an std::map containing keys and values + // of the same type. Since std::map cannot represent the null state, the + // std::map will be empty if Map is null. The Map will always be left in a + // non-null state. + void Swap(std::map<Key, Value>* other) { + is_null_ = false; + map_.swap(*other); + } + + // Removes all contents from the Map and places them into parallel key/value + // arrays. Each key will be copied from the source to the destination, and + // values will be copied unless their type is designated move-only, in which + // case they will be passed by calling their Pass() method. Either way, the + // Map will be left in a null state. + void DecomposeMapTo(mojo::Array<Key>* keys, mojo::Array<Value>* values) { + Traits::Decompose(&map_, keys, values); + Traits::Finalize(&map_); + map_.clear(); + is_null_ = true; + } + + // Returns a new Map that contains a copy of the contents of this map. If the + // values are of a type that is designated move-only, they will be cloned + // using the Clone() method of the type. Please note that calling this method + // will fail compilation if the value type cannot be cloned (which usually + // means that it is a Mojo handle type or a type that contains Mojo handles). + Map Clone() const { + Map result; + result.is_null_ = is_null_; + Traits::Clone(map_, &result.map_); + return result.Pass(); + } + + // Indicates whether the contents of this map are equal to those of another + // Map (including nullness). Keys are compared by the != operator. Values are + // compared as follows: + // - Map, Array, Struct, or StructPtr values are compared by their Equals() + // method. + // - ScopedHandleBase-derived types are compared by their handles. + // - Values of other types are compared by their "==" operator. + bool Equals(const Map& other) const { + if (is_null() != other.is_null()) + return false; + if (size() != other.size()) + return false; + auto i = begin(); + auto j = other.begin(); + while (i != end()) { + if (i.GetKey() != j.GetKey()) + return false; + if (!internal::ValueTraits<Value>::Equals(i.GetValue(), j.GetValue())) + return false; + ++i; + ++j; + } + return true; + } + + // A read-only iterator for Map. + class ConstMapIterator { + public: + ConstMapIterator( + const typename std::map<KeyStorageType, + ValueStorageType>::const_iterator& it) + : it_(it) {} + + // Returns a const reference to the key and value. + KeyConstRefType GetKey() { return Traits::GetKey(it_); } + ValueConstRefType GetValue() { return Traits::GetValue(it_); } + + ConstMapIterator& operator*() { + return *this; + } + ConstMapIterator& operator++() { + it_++; + return *this; + } + bool operator!=(const ConstMapIterator& rhs) const { + return it_ != rhs.it_; + } + bool operator==(const ConstMapIterator& rhs) const { + return it_ == rhs.it_; + } + + private: + typename std::map<KeyStorageType, ValueStorageType>::const_iterator it_; + }; + + // Provide read-only iteration over map members in a way similar to STL + // collections. + ConstMapIterator begin() const { return ConstMapIterator(map_.begin()); } + ConstMapIterator end() const { return ConstMapIterator(map_.end()); } + + // Returns the iterator pointing to the entry for |key|, if present, or else + // returns end(). + ConstMapIterator find(KeyForwardType key) const { + return ConstMapIterator(map_.find(key)); + } + + private: + typedef std::map<KeyStorageType, ValueStorageType> Map::*Testable; + + public: + // The Map may be used in boolean expressions to determine if it is non-null, + // but is not implicitly convertible to an actual bool value (which would be + // dangerous). + operator Testable() const { return is_null_ ? 0 : &Map::map_; } + + private: + // Forbid the == and != operators explicitly, otherwise Map will be converted + // to Testable to do == or != comparison. + template <typename T, typename U> + bool operator==(const Map<T, U>& other) const = delete; + template <typename T, typename U> + bool operator!=(const Map<T, U>& other) const = delete; + + void Take(Map* other) { + reset(); + Swap(other); + } + + std::map<KeyStorageType, ValueStorageType> map_; + bool is_null_; +}; + +// Copies the contents of an std::map to a new Map, optionally changing the +// types of the keys and values along the way using TypeConverter. +template <typename MojoKey, + typename MojoValue, + typename STLKey, + typename STLValue> +struct TypeConverter<Map<MojoKey, MojoValue>, std::map<STLKey, STLValue>> { + static Map<MojoKey, MojoValue> Convert( + const std::map<STLKey, STLValue>& input) { + Map<MojoKey, MojoValue> result; + result.mark_non_null(); + for (auto& pair : input) { + result.insert(TypeConverter<MojoKey, STLKey>::Convert(pair.first), + TypeConverter<MojoValue, STLValue>::Convert(pair.second)); + } + return result.Pass(); + } +}; + +// Copies the contents of a Map to an std::map, optionally changing the types of +// the keys and values along the way using TypeConverter. +template <typename MojoKey, + typename MojoValue, + typename STLKey, + typename STLValue> +struct TypeConverter<std::map<STLKey, STLValue>, Map<MojoKey, MojoValue>> { + static std::map<STLKey, STLValue> Convert( + const Map<MojoKey, MojoValue>& input) { + std::map<STLKey, STLValue> result; + if (!input.is_null()) { + for (auto it = input.begin(); it != input.end(); ++it) { + result.insert(std::make_pair( + TypeConverter<STLKey, MojoKey>::Convert(it.GetKey()), + TypeConverter<STLValue, MojoValue>::Convert(it.GetValue()))); + } + } + return result; + } +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_MAP_H_ diff --git a/mojo/public/cpp/bindings/message.h b/mojo/public/cpp/bindings/message.h new file mode 100644 index 0000000..7e34606 --- /dev/null +++ b/mojo/public/cpp/bindings/message.h @@ -0,0 +1,159 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_H_ + +#include <vector> + +#include "mojo/public/cpp/bindings/lib/message_internal.h" +#include "mojo/public/cpp/environment/logging.h" + +namespace mojo { + +// Message is a holder for the data and handles to be sent over a MessagePipe. +// Message owns its data and handles, but a consumer of Message is free to +// mutate the data and handles. The message's data is comprised of a header +// followed by payload. +class Message { + public: + Message(); + ~Message(); + + void Reset(); + + void AllocData(uint32_t num_bytes); + void AllocUninitializedData(uint32_t num_bytes); + + // Transfers data and handles to |destination|. + void MoveTo(Message* destination); + + uint32_t data_num_bytes() const { return data_num_bytes_; } + + // Access the raw bytes of the message. + const uint8_t* data() const { + return reinterpret_cast<const uint8_t*>(data_); + } + uint8_t* mutable_data() { return reinterpret_cast<uint8_t*>(data_); } + + // Access the header. + const internal::MessageHeader* header() const { return &data_->header; } + + uint32_t name() const { return data_->header.name; } + bool has_flag(uint32_t flag) const { return !!(data_->header.flags & flag); } + + // Access the request_id field (if present). + bool has_request_id() const { return data_->header.version >= 1; } + uint64_t request_id() const { + MOJO_DCHECK(has_request_id()); + return static_cast<const internal::MessageHeaderWithRequestID*>( + &data_->header)->request_id; + } + void set_request_id(uint64_t request_id) { + MOJO_DCHECK(has_request_id()); + static_cast<internal::MessageHeaderWithRequestID*>(&data_->header) + ->request_id = request_id; + } + + // Access the payload. + const uint8_t* payload() const { + return reinterpret_cast<const uint8_t*>(data_) + data_->header.num_bytes; + } + uint8_t* mutable_payload() { + return reinterpret_cast<uint8_t*>(data_) + data_->header.num_bytes; + } + uint32_t payload_num_bytes() const { + MOJO_DCHECK(data_num_bytes_ >= data_->header.num_bytes); + return data_num_bytes_ - data_->header.num_bytes; + } + + // Access the handles. + const std::vector<Handle>* handles() const { return &handles_; } + std::vector<Handle>* mutable_handles() { return &handles_; } + + private: + void Initialize(); + void FreeDataAndCloseHandles(); + + uint32_t data_num_bytes_; + internal::MessageData* data_; + std::vector<Handle> handles_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(Message); +}; + +class MessageReceiver { + public: + virtual ~MessageReceiver() {} + + // The receiver may mutate the given message. Returns true if the message + // was accepted and false otherwise, indicating that the message was invalid + // or malformed. + virtual bool Accept(Message* message) MOJO_WARN_UNUSED_RESULT = 0; +}; + +class MessageReceiverWithResponder : public MessageReceiver { + public: + ~MessageReceiverWithResponder() override {} + + // A variant on Accept that registers a MessageReceiver (known as the + // responder) to handle the response message generated from the given + // message. The responder's Accept method may be called during + // AcceptWithResponder or some time after its return. + // + // NOTE: Upon returning true, AcceptWithResponder assumes ownership of + // |responder| and will delete it after calling |responder->Accept| or upon + // its own destruction. + // + virtual bool AcceptWithResponder(Message* message, MessageReceiver* responder) + MOJO_WARN_UNUSED_RESULT = 0; +}; + +// A MessageReceiver that is also able to provide status about the state +// of the underlying MessagePipe to which it will be forwarding messages +// received via the |Accept()| call. +class MessageReceiverWithStatus : public MessageReceiver { + public: + ~MessageReceiverWithStatus() override {} + + // Returns |true| if this MessageReceiver is currently bound to a MessagePipe, + // the pipe has not been closed, and the pipe has not encountered an error. + virtual bool IsValid() = 0; +}; + +// An alternative to MessageReceiverWithResponder for cases in which it +// is necessary for the implementor of this interface to know about the status +// of the MessagePipe which will carry the responses. +class MessageReceiverWithResponderStatus : public MessageReceiver { + public: + ~MessageReceiverWithResponderStatus() override {} + + // A variant on Accept that registers a MessageReceiverWithStatus (known as + // the responder) to handle the response message generated from the given + // message. Any of the responder's methods (Accept or IsValid) may be called + // during AcceptWithResponder or some time after its return. + // + // NOTE: Upon returning true, AcceptWithResponder assumes ownership of + // |responder| and will delete it after calling |responder->Accept| or upon + // its own destruction. + // + virtual bool AcceptWithResponder(Message* message, + MessageReceiverWithStatus* responder) + MOJO_WARN_UNUSED_RESULT = 0; +}; + +// Read a single message from the pipe and dispatch to the given receiver. The +// receiver may be null, in which case the message is simply discarded. +// Returns MOJO_RESULT_SHOULD_WAIT if the caller should wait on the handle to +// become readable. Returns MOJO_RESULT_OK if a message was dispatched and +// otherwise returns an error code if something went wrong. +// +// NOTE: The message hasn't been validated and may be malformed! +MojoResult ReadAndDispatchMessage(MessagePipeHandle handle, + MessageReceiver* receiver, + bool* receiver_result); + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_H_ diff --git a/mojo/public/cpp/bindings/message_filter.h b/mojo/public/cpp/bindings/message_filter.h new file mode 100644 index 0000000..8e10f6b --- /dev/null +++ b/mojo/public/cpp/bindings/message_filter.h @@ -0,0 +1,39 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_FILTER_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_FILTER_H_ + +#include "mojo/public/cpp/bindings/message.h" +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { + +// This class is the base class for message filters. Subclasses should +// implement the pure virtual method Accept() inherited from MessageReceiver to +// process messages and/or forward them to |sink_|. +class MessageFilter : public MessageReceiver { + public: + // Doesn't take ownership of |sink|. Therefore |sink| has to stay alive while + // this object is alive. + explicit MessageFilter(MessageReceiver* sink = nullptr); + ~MessageFilter() override; + + void set_sink(MessageReceiver* sink) { sink_ = sink; } + + protected: + MessageReceiver* sink_; +}; + +// A trivial filter that simply forwards every message it receives to |sink_|. +class PassThroughFilter : public MessageFilter { + public: + explicit PassThroughFilter(MessageReceiver* sink = nullptr); + + bool Accept(Message* message) override; +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_FILTER_H_ diff --git a/mojo/public/cpp/bindings/no_interface.h b/mojo/public/cpp/bindings/no_interface.h new file mode 100644 index 0000000..d8915cd --- /dev/null +++ b/mojo/public/cpp/bindings/no_interface.h @@ -0,0 +1,52 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_NO_INTERFACE_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_NO_INTERFACE_H_ + +#include "mojo/public/cpp/bindings/message.h" +#include "mojo/public/cpp/bindings/message_filter.h" +#include "mojo/public/cpp/system/core.h" + +namespace mojo { + +// NoInterface is for use in cases when a non-existent or empty interface is +// needed. + +class NoInterfaceProxy; +class NoInterfaceStub; + +class NoInterface { + public: + static const char* Name_; + typedef NoInterfaceProxy Proxy_; + typedef NoInterfaceStub Stub_; + typedef PassThroughFilter RequestValidator_; + typedef PassThroughFilter ResponseValidator_; + virtual ~NoInterface() {} +}; + +class NoInterfaceProxy : public NoInterface { + public: + explicit NoInterfaceProxy(MessageReceiver* receiver) {} +}; + +class NoInterfaceStub : public MessageReceiverWithResponder { + public: + NoInterfaceStub() {} + void set_sink(NoInterface* sink) {} + NoInterface* sink() { return nullptr; } + bool Accept(Message* message) override; + bool AcceptWithResponder(Message* message, + MessageReceiver* responder) override; +}; + +// AnyInterface is for use in cases where any interface would do (e.g., see the +// Shell::Connect method). + +typedef NoInterface AnyInterface; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_NO_INTERFACE_H_ diff --git a/mojo/public/cpp/bindings/string.h b/mojo/public/cpp/bindings/string.h new file mode 100644 index 0000000..e0ed4ba --- /dev/null +++ b/mojo/public/cpp/bindings/string.h @@ -0,0 +1,175 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_STRING_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_STRING_H_ + +#include <string> + +#include "mojo/public/cpp/bindings/lib/array_internal.h" +#include "mojo/public/cpp/bindings/type_converter.h" +#include "mojo/public/cpp/environment/logging.h" + +namespace mojo { + +// A UTF-8 encoded character string that can be null. Provides functions that +// are similar to std::string, along with access to the underlying std::string +// object. +class String { + public: + typedef internal::String_Data Data_; + + String() : is_null_(true) {} + String(const std::string& str) : value_(str), is_null_(false) {} + String(const char* chars) : is_null_(!chars) { + if (chars) + value_ = chars; + } + String(const char* chars, size_t num_chars) + : value_(chars, num_chars), is_null_(false) {} + String(const mojo::String& str) + : value_(str.value_), is_null_(str.is_null_) {} + + template <size_t N> + String(const char chars[N]) + : value_(chars, N - 1), is_null_(false) {} + + template <typename U> + static String From(const U& other) { + return TypeConverter<String, U>::Convert(other); + } + + template <typename U> + U To() const { + return TypeConverter<U, String>::Convert(*this); + } + + String& operator=(const mojo::String& str) { + value_ = str.value_; + is_null_ = str.is_null_; + return *this; + } + String& operator=(const std::string& str) { + value_ = str; + is_null_ = false; + return *this; + } + String& operator=(const char* chars) { + is_null_ = !chars; + if (chars) { + value_ = chars; + } else { + value_.clear(); + } + return *this; + } + + void reset() { + value_.clear(); + is_null_ = true; + } + + bool is_null() const { return is_null_; } + + size_t size() const { return value_.size(); } + + const char* data() const { return value_.data(); } + + const char& at(size_t offset) const { return value_.at(offset); } + const char& operator[](size_t offset) const { return value_[offset]; } + + const std::string& get() const { return value_; } + operator const std::string&() const { return value_; } + + void Swap(String* other) { + std::swap(is_null_, other->is_null_); + value_.swap(other->value_); + } + + void Swap(std::string* other) { + is_null_ = false; + value_.swap(*other); + } + + private: + typedef std::string String::*Testable; + + public: + operator Testable() const { return is_null_ ? 0 : &String::value_; } + + private: + std::string value_; + bool is_null_; +}; + +inline bool operator==(const String& a, const String& b) { + return a.is_null() == b.is_null() && a.get() == b.get(); +} +inline bool operator==(const char* a, const String& b) { + return !b.is_null() && a == b.get(); +} +inline bool operator==(const String& a, const char* b) { + return !a.is_null() && a.get() == b; +} +inline bool operator!=(const String& a, const String& b) { + return !(a == b); +} +inline bool operator!=(const char* a, const String& b) { + return !(a == b); +} +inline bool operator!=(const String& a, const char* b) { + return !(a == b); +} + +inline std::ostream& operator<<(std::ostream& out, const String& s) { + return out << s.get(); +} + +inline bool operator<(const String& a, const String& b) { + if (a.is_null()) + return !b.is_null(); + if (b.is_null()) + return false; + + return a.get() < b.get(); +} + +// TODO(darin): Add similar variants of operator<,<=,>,>= + +template <> +struct TypeConverter<String, std::string> { + static String Convert(const std::string& input) { return String(input); } +}; + +template <> +struct TypeConverter<std::string, String> { + static std::string Convert(const String& input) { return input; } +}; + +template <size_t N> +struct TypeConverter<String, char[N]> { + static String Convert(const char input[N]) { + MOJO_DCHECK(input); + return String(input, N - 1); + } +}; + +// Appease MSVC. +template <size_t N> +struct TypeConverter<String, const char[N]> { + static String Convert(const char input[N]) { + MOJO_DCHECK(input); + return String(input, N - 1); + } +}; + +template <> +struct TypeConverter<String, const char*> { + // |input| may be null, in which case a null String will be returned. + static String Convert(const char* input) { return String(input); } +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_STRING_H_ diff --git a/mojo/public/cpp/bindings/strong_binding.h b/mojo/public/cpp/bindings/strong_binding.h new file mode 100644 index 0000000..860260e --- /dev/null +++ b/mojo/public/cpp/bindings/strong_binding.h @@ -0,0 +1,127 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_STRONG_BINDING_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_STRONG_BINDING_H_ + +#include <assert.h> + +#include "mojo/public/c/environment/async_waiter.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/callback.h" +#include "mojo/public/cpp/bindings/interface_ptr.h" +#include "mojo/public/cpp/bindings/interface_request.h" +#include "mojo/public/cpp/bindings/lib/filter_chain.h" +#include "mojo/public/cpp/bindings/lib/message_header_validator.h" +#include "mojo/public/cpp/bindings/lib/router.h" +#include "mojo/public/cpp/system/core.h" + +namespace mojo { + +// This connects an interface implementation strongly to a pipe. When a +// connection error is detected the implementation is deleted. Deleting the +// connector also closes the pipe. +// +// Example of an implementation that is always bound strongly to a pipe +// +// class StronglyBound : public Foo { +// public: +// explicit StronglyBound(InterfaceRequest<Foo> request) +// : binding_(this, request.Pass()) {} +// +// // Foo implementation here +// +// private: +// StrongBinding<Foo> binding_; +// }; +// +// class MyFooFactory : public InterfaceFactory<Foo> { +// public: +// void Create(..., InterfaceRequest<Foo> request) override { +// new StronglyBound(request.Pass()); // The binding now owns the +// // instance of StronglyBound. +// } +// }; +template <typename Interface> +class StrongBinding { + MOJO_MOVE_ONLY_TYPE(StrongBinding) + + public: + explicit StrongBinding(Interface* impl) : binding_(impl) { + binding_.set_connection_error_handler([this]() { OnConnectionError(); }); + } + + StrongBinding( + Interface* impl, + ScopedMessagePipeHandle handle, + const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) + : StrongBinding(impl) { + binding_.Bind(handle.Pass(), waiter); + } + + StrongBinding( + Interface* impl, + InterfacePtr<Interface>* ptr, + const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) + : StrongBinding(impl) { + binding_.Bind(ptr, waiter); + } + + StrongBinding( + Interface* impl, + InterfaceRequest<Interface> request, + const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) + : StrongBinding(impl) { + binding_.Bind(request.Pass(), waiter); + } + + ~StrongBinding() {} + + void Bind( + ScopedMessagePipeHandle handle, + const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) { + assert(!binding_.is_bound()); + binding_.Bind(handle.Pass(), waiter); + } + + void Bind( + InterfacePtr<Interface>* ptr, + const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) { + assert(!binding_.is_bound()); + binding_.Bind(ptr, waiter); + } + + void Bind( + InterfaceRequest<Interface> request, + const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) { + assert(!binding_.is_bound()); + binding_.Bind(request.Pass(), waiter); + } + + bool WaitForIncomingMethodCall() { + return binding_.WaitForIncomingMethodCall(); + } + + // Note: The error handler must not delete the interface implementation. + void set_connection_error_handler(const Closure& error_handler) { + connection_error_handler_ = error_handler; + } + + Interface* impl() { return binding_.impl(); } + // Exposed for testing, should not generally be used. + internal::Router* internal_router() { return binding_.internal_router(); } + + void OnConnectionError() { + connection_error_handler_.Run(); + delete binding_.impl(); + } + + private: + Closure connection_error_handler_; + Binding<Interface> binding_; +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_STRONG_BINDING_H_ diff --git a/mojo/public/cpp/bindings/struct_ptr.h b/mojo/public/cpp/bindings/struct_ptr.h new file mode 100644 index 0000000..b324e93 --- /dev/null +++ b/mojo/public/cpp/bindings/struct_ptr.h @@ -0,0 +1,206 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_STRUCT_PTR_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_STRUCT_PTR_H_ + +#include <new> + +#include "mojo/public/cpp/bindings/type_converter.h" +#include "mojo/public/cpp/environment/logging.h" +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { +namespace internal { + +template <typename Struct> +class StructHelper { + public: + template <typename Ptr> + static void Initialize(Ptr* ptr) { + ptr->Initialize(); + } +}; + +} // namespace internal + +// Smart pointer wrapping a mojom structure with move-only semantics. +template <typename Struct> +class StructPtr { + MOJO_MOVE_ONLY_TYPE(StructPtr) + + public: + + StructPtr() : ptr_(nullptr) {} + StructPtr(decltype(nullptr)) : ptr_(nullptr) {} + + ~StructPtr() { delete ptr_; } + + StructPtr& operator=(decltype(nullptr)) { + reset(); + return *this; + } + + StructPtr(StructPtr&& other) : ptr_(nullptr) { Take(&other); } + StructPtr& operator=(StructPtr&& other) { + Take(&other); + return *this; + } + + template <typename U> + U To() const { + return TypeConverter<U, StructPtr>::Convert(*this); + } + + void reset() { + if (ptr_) { + delete ptr_; + ptr_ = nullptr; + } + } + + bool is_null() const { return ptr_ == nullptr; } + + Struct& operator*() const { + MOJO_DCHECK(ptr_); + return *ptr_; + } + Struct* operator->() const { + MOJO_DCHECK(ptr_); + return ptr_; + } + Struct* get() const { return ptr_; } + + void Swap(StructPtr* other) { std::swap(ptr_, other->ptr_); } + + // Please note that calling this method will fail compilation if the value + // type |Struct| doesn't have a Clone() method defined (which usually means + // that it contains Mojo handles). + StructPtr Clone() const { return is_null() ? StructPtr() : ptr_->Clone(); } + + bool Equals(const StructPtr& other) const { + if (is_null() || other.is_null()) + return is_null() && other.is_null(); + return ptr_->Equals(*other.ptr_); + } + + private: + typedef Struct* StructPtr::*Testable; + + public: + operator Testable() const { return ptr_ ? &StructPtr::ptr_ : 0; } + + private: + friend class internal::StructHelper<Struct>; + + // Forbid the == and != operators explicitly, otherwise StructPtr will be + // converted to Testable to do == or != comparison. + template <typename T> + bool operator==(const StructPtr<T>& other) const = delete; + template <typename T> + bool operator!=(const StructPtr<T>& other) const = delete; + + void Initialize() { + MOJO_DCHECK(!ptr_); + ptr_ = new Struct(); + } + + void Take(StructPtr* other) { + reset(); + Swap(other); + } + + Struct* ptr_; +}; + +// Designed to be used when Struct is small and copyable. +template <typename Struct> +class InlinedStructPtr { + MOJO_MOVE_ONLY_TYPE(InlinedStructPtr); + + public: + + InlinedStructPtr() : is_null_(true) {} + InlinedStructPtr(decltype(nullptr)) : is_null_(true) {} + + ~InlinedStructPtr() {} + + InlinedStructPtr& operator=(decltype(nullptr)) { + reset(); + return *this; + } + + InlinedStructPtr(InlinedStructPtr&& other) : is_null_(true) { Take(&other); } + InlinedStructPtr& operator=(InlinedStructPtr&& other) { + Take(&other); + return *this; + } + + template <typename U> + U To() const { + return TypeConverter<U, InlinedStructPtr>::Convert(*this); + } + + void reset() { + is_null_ = true; + value_. ~Struct(); + new (&value_) Struct(); + } + + bool is_null() const { return is_null_; } + + Struct& operator*() const { + MOJO_DCHECK(!is_null_); + return value_; + } + Struct* operator->() const { + MOJO_DCHECK(!is_null_); + return &value_; + } + Struct* get() const { return &value_; } + + void Swap(InlinedStructPtr* other) { + std::swap(value_, other->value_); + std::swap(is_null_, other->is_null_); + } + + InlinedStructPtr Clone() const { + return is_null() ? InlinedStructPtr() : value_.Clone(); + } + bool Equals(const InlinedStructPtr& other) const { + if (is_null() || other.is_null()) + return is_null() && other.is_null(); + return value_.Equals(other.value_); + } + + private: + typedef Struct InlinedStructPtr::*Testable; + + public: + operator Testable() const { return is_null_ ? 0 : &InlinedStructPtr::value_; } + + private: + friend class internal::StructHelper<Struct>; + + // Forbid the == and != operators explicitly, otherwise InlinedStructPtr will + // be converted to Testable to do == or != comparison. + template <typename T> + bool operator==(const InlinedStructPtr<T>& other) const = delete; + template <typename T> + bool operator!=(const InlinedStructPtr<T>& other) const = delete; + + void Initialize() { is_null_ = false; } + + void Take(InlinedStructPtr* other) { + reset(); + Swap(other); + } + + mutable Struct value_; + bool is_null_; +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_STRUCT_PTR_H_ diff --git a/mojo/public/cpp/bindings/tests/BUILD.gn b/mojo/public/cpp/bindings/tests/BUILD.gn new file mode 100644 index 0000000..1bc018c --- /dev/null +++ b/mojo/public/cpp/bindings/tests/BUILD.gn @@ -0,0 +1,86 @@ +# 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. + +import("../../../mojo_application.gni") +import("../../../mojo_sdk.gni") + +mojo_sdk_source_set("tests") { + testonly = true + + sources = [ + "array_unittest.cc", + "binding_callback_unittest.cc", + "binding_unittest.cc", + "bounds_checker_unittest.cc", + "buffer_unittest.cc", + "callback_unittest.cc", + "connector_unittest.cc", + "constant_unittest.cc", + "container_test_util.cc", + "equals_unittest.cc", + "handle_passing_unittest.cc", + "interface_ptr_unittest.cc", + "map_unittest.cc", + "message_queue.cc", + "message_queue.h", + "request_response_unittest.cc", + "router_unittest.cc", + "sample_service_unittest.cc", + "serialization_warning_unittest.cc", + "string_unittest.cc", + "struct_unittest.cc", + "type_conversion_unittest.cc", + "union_unittest.cc", + "validation_unittest.cc", + ] + + deps = [ + ":mojo_public_bindings_test_utils", + "//mojo/environment:chromium", + "//mojo/message_pump", + "//testing/gtest", + ] + + mojo_sdk_deps = [ + "mojo/public/cpp/bindings", + "mojo/public/cpp/bindings:callback", + "mojo/public/cpp/system", + "mojo/public/cpp/test_support:test_utils", + "mojo/public/cpp/utility", + "mojo/public/interfaces/bindings/tests:test_associated_interfaces", + "mojo/public/interfaces/bindings/tests:test_interfaces", + "mojo/public/interfaces/bindings/tests:test_interfaces_experimental", + ] +} + +mojo_sdk_source_set("perftests") { + testonly = true + + sources = [ + "bindings_perftest.cc", + ] + + deps = [ + "//testing/gtest", + ] + + mojo_sdk_deps = [ + "mojo/public/cpp/bindings", + "mojo/public/cpp/bindings:callback", + "mojo/public/cpp/environment:standalone", + "mojo/public/cpp/system", + "mojo/public/cpp/test_support:test_utils", + "mojo/public/cpp/utility", + "mojo/public/interfaces/bindings/tests:test_interfaces", + ] +} + +mojo_sdk_source_set("mojo_public_bindings_test_utils") { + sources = [ + "validation_test_input_parser.cc", + "validation_test_input_parser.h", + ] + + mojo_sdk_deps = [ "mojo/public/c/system" ] +} diff --git a/mojo/public/cpp/bindings/tests/array_unittest.cc b/mojo/public/cpp/bindings/tests/array_unittest.cc new file mode 100644 index 0000000..8befe81 --- /dev/null +++ b/mojo/public/cpp/bindings/tests/array_unittest.cc @@ -0,0 +1,438 @@ +// 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. + +#include "mojo/public/cpp/bindings/array.h" +#include "mojo/public/cpp/bindings/lib/array_internal.h" +#include "mojo/public/cpp/bindings/lib/array_serialization.h" +#include "mojo/public/cpp/bindings/lib/fixed_buffer.h" +#include "mojo/public/cpp/bindings/tests/container_test_util.h" +#include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace test { +namespace { + +using mojo::internal::Array_Data; +using mojo::internal::ArrayValidateParams; +using mojo::internal::FixedBufferForTesting; +using mojo::internal::String_Data; + +using ArrayTest = testing::Test; + +// Tests that basic Array operations work. +TEST_F(ArrayTest, Basic) { + Array<char> array(8); + for (size_t i = 0; i < array.size(); ++i) { + char val = static_cast<char>(i * 2); + array[i] = val; + EXPECT_EQ(val, array.at(i)); + } +} + +// Tests that basic Array<bool> operations work. +TEST_F(ArrayTest, Bool) { + Array<bool> array(64); + for (size_t i = 0; i < array.size(); ++i) { + bool val = i % 3 == 0; + array[i] = val; + EXPECT_EQ(val, array.at(i)); + } +} + +// Tests that Array<ScopedMessagePipeHandle> supports transferring handles. +TEST_F(ArrayTest, Handle) { + MessagePipe pipe; + Array<ScopedMessagePipeHandle> handles(2); + handles[0] = pipe.handle0.Pass(); + handles[1].reset(pipe.handle1.release()); + + EXPECT_FALSE(pipe.handle0.is_valid()); + EXPECT_FALSE(pipe.handle1.is_valid()); + + Array<ScopedMessagePipeHandle> handles2 = handles.Pass(); + EXPECT_TRUE(handles2[0].is_valid()); + EXPECT_TRUE(handles2[1].is_valid()); + + ScopedMessagePipeHandle pipe_handle = handles2[0].Pass(); + EXPECT_TRUE(pipe_handle.is_valid()); + EXPECT_FALSE(handles2[0].is_valid()); +} + +// Tests that Array<ScopedMessagePipeHandle> supports closing handles. +TEST_F(ArrayTest, HandlesAreClosed) { + MessagePipe pipe; + MojoHandle pipe0_value = pipe.handle0.get().value(); + MojoHandle pipe1_value = pipe.handle0.get().value(); + + { + Array<ScopedMessagePipeHandle> handles(2); + handles[0] = pipe.handle0.Pass(); + handles[1].reset(pipe.handle0.release()); + } + + // We expect the pipes to have been closed. + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(pipe0_value)); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(pipe1_value)); +} + +TEST_F(ArrayTest, Clone) { + { + // Test POD. + Array<int32_t> array(3); + for (size_t i = 0; i < array.size(); ++i) + array[i] = static_cast<int32_t>(i); + + Array<int32_t> clone_array = array.Clone(); + EXPECT_EQ(array.size(), clone_array.size()); + for (size_t i = 0; i < array.size(); ++i) + EXPECT_EQ(array[i], clone_array[i]); + } + + { + // Test copyable object. + Array<String> array(2); + array[0] = "hello"; + array[1] = "world"; + + Array<String> clone_array = array.Clone(); + EXPECT_EQ(array.size(), clone_array.size()); + for (size_t i = 0; i < array.size(); ++i) + EXPECT_EQ(array[i], clone_array[i]); + } + + { + // Test struct. + Array<RectPtr> array(2); + array[1] = Rect::New(); + array[1]->x = 1; + array[1]->y = 2; + array[1]->width = 3; + array[1]->height = 4; + + Array<RectPtr> clone_array = array.Clone(); + EXPECT_EQ(array.size(), clone_array.size()); + EXPECT_TRUE(clone_array[0].is_null()); + EXPECT_EQ(array[1]->x, clone_array[1]->x); + EXPECT_EQ(array[1]->y, clone_array[1]->y); + EXPECT_EQ(array[1]->width, clone_array[1]->width); + EXPECT_EQ(array[1]->height, clone_array[1]->height); + } + + { + // Test array of array. + Array<Array<int8_t>> array(2); + array[1] = Array<int8_t>(2); + array[1][0] = 0; + array[1][1] = 1; + + Array<Array<int8_t>> clone_array = array.Clone(); + EXPECT_EQ(array.size(), clone_array.size()); + EXPECT_TRUE(clone_array[0].is_null()); + EXPECT_EQ(array[1].size(), clone_array[1].size()); + EXPECT_EQ(array[1][0], clone_array[1][0]); + EXPECT_EQ(array[1][1], clone_array[1][1]); + } + + { + // Test that array of handles still works although Clone() is not available. + Array<ScopedMessagePipeHandle> array(10); + EXPECT_FALSE(array[0].is_valid()); + } +} + +TEST_F(ArrayTest, Serialization_ArrayOfPOD) { + Array<int32_t> array(4); + for (size_t i = 0; i < array.size(); ++i) + array[i] = static_cast<int32_t>(i); + + size_t size = GetSerializedSize_(array); + EXPECT_EQ(8U + 4 * 4U, size); + + FixedBufferForTesting buf(size); + Array_Data<int32_t>* data; + ArrayValidateParams validate_params(0, false, nullptr); + SerializeArray_(array.Pass(), &buf, &data, &validate_params); + + Array<int32_t> array2; + Deserialize_(data, &array2); + + EXPECT_EQ(4U, array2.size()); + for (size_t i = 0; i < array2.size(); ++i) + EXPECT_EQ(static_cast<int32_t>(i), array2[i]); +} + +TEST_F(ArrayTest, Serialization_EmptyArrayOfPOD) { + Array<int32_t> array(0); + size_t size = GetSerializedSize_(array); + EXPECT_EQ(8U, size); + + FixedBufferForTesting buf(size); + Array_Data<int32_t>* data; + ArrayValidateParams validate_params(0, false, nullptr); + SerializeArray_(array.Pass(), &buf, &data, &validate_params); + + Array<int32_t> array2; + Deserialize_(data, &array2); + EXPECT_EQ(0U, array2.size()); +} + +TEST_F(ArrayTest, Serialization_ArrayOfArrayOfPOD) { + Array<Array<int32_t>> array(2); + for (size_t j = 0; j < array.size(); ++j) { + Array<int32_t> inner(4); + for (size_t i = 0; i < inner.size(); ++i) + inner[i] = static_cast<int32_t>(i + (j * 10)); + array[j] = inner.Pass(); + } + + size_t size = GetSerializedSize_(array); + EXPECT_EQ(8U + 2 * 8U + 2 * (8U + 4 * 4U), size); + + FixedBufferForTesting buf(size); + Array_Data<Array_Data<int32_t>*>* data; + ArrayValidateParams validate_params( + 0, false, new ArrayValidateParams(0, false, nullptr)); + SerializeArray_(array.Pass(), &buf, &data, &validate_params); + + Array<Array<int32_t>> array2; + Deserialize_(data, &array2); + + EXPECT_EQ(2U, array2.size()); + for (size_t j = 0; j < array2.size(); ++j) { + const Array<int32_t>& inner = array2[j]; + EXPECT_EQ(4U, inner.size()); + for (size_t i = 0; i < inner.size(); ++i) + EXPECT_EQ(static_cast<int32_t>(i + (j * 10)), inner[i]); + } +} + +TEST_F(ArrayTest, Serialization_ArrayOfBool) { + Array<bool> array(10); + for (size_t i = 0; i < array.size(); ++i) + array[i] = i % 2 ? true : false; + + size_t size = GetSerializedSize_(array); + EXPECT_EQ(8U + 8U, size); + + FixedBufferForTesting buf(size); + Array_Data<bool>* data; + ArrayValidateParams validate_params(0, false, nullptr); + SerializeArray_(array.Pass(), &buf, &data, &validate_params); + + Array<bool> array2; + Deserialize_(data, &array2); + + EXPECT_EQ(10U, array2.size()); + for (size_t i = 0; i < array2.size(); ++i) + EXPECT_EQ(i % 2 ? true : false, array2[i]); +} + +TEST_F(ArrayTest, Serialization_ArrayOfString) { + Array<String> array(10); + for (size_t i = 0; i < array.size(); ++i) { + char c = 'A' + static_cast<char>(i); + array[i] = String(&c, 1); + } + + size_t size = GetSerializedSize_(array); + EXPECT_EQ(8U + // array header + 10 * 8U + // array payload (10 pointers) + 10 * (8U + // string header + 8U), // string length of 1 padded to 8 + size); + + FixedBufferForTesting buf(size); + Array_Data<String_Data*>* data; + ArrayValidateParams validate_params( + 0, false, new ArrayValidateParams(0, false, nullptr)); + SerializeArray_(array.Pass(), &buf, &data, &validate_params); + + Array<String> array2; + Deserialize_(data, &array2); + + EXPECT_EQ(10U, array2.size()); + for (size_t i = 0; i < array2.size(); ++i) { + char c = 'A' + static_cast<char>(i); + EXPECT_EQ(String(&c, 1), array2[i]); + } +} + +TEST_F(ArrayTest, Resize_Copyable) { + ASSERT_EQ(0u, CopyableType::num_instances()); + mojo::Array<CopyableType> array(3); + std::vector<CopyableType*> value_ptrs; + value_ptrs.push_back(array[0].ptr()); + value_ptrs.push_back(array[1].ptr()); + + for (size_t i = 0; i < array.size(); i++) + array[i].ResetCopied(); + + array.resize(2); + ASSERT_EQ(2u, array.size()); + EXPECT_EQ(array.size(), CopyableType::num_instances()); + for (size_t i = 0; i < array.size(); i++) { + EXPECT_FALSE(array[i].copied()); + EXPECT_EQ(value_ptrs[i], array[i].ptr()); + } + + array.resize(3); + array[2].ResetCopied(); + ASSERT_EQ(3u, array.size()); + EXPECT_EQ(array.size(), CopyableType::num_instances()); + for (size_t i = 0; i < array.size(); i++) + EXPECT_FALSE(array[i].copied()); + value_ptrs.push_back(array[2].ptr()); + + size_t capacity = array.storage().capacity(); + array.resize(capacity); + ASSERT_EQ(capacity, array.size()); + EXPECT_EQ(array.size(), CopyableType::num_instances()); + for (size_t i = 0; i < 3; i++) + EXPECT_FALSE(array[i].copied()); + for (size_t i = 3; i < array.size(); i++) { + array[i].ResetCopied(); + value_ptrs.push_back(array[i].ptr()); + } + + array.resize(capacity + 2); + ASSERT_EQ(capacity + 2, array.size()); + EXPECT_EQ(array.size(), CopyableType::num_instances()); + for (size_t i = 0; i < capacity; i++) { + EXPECT_TRUE(array[i].copied()); + EXPECT_EQ(value_ptrs[i], array[i].ptr()); + } + array.reset(); + EXPECT_EQ(0u, CopyableType::num_instances()); + EXPECT_FALSE(array); + array.resize(0); + EXPECT_EQ(0u, CopyableType::num_instances()); + EXPECT_TRUE(array); +} + +TEST_F(ArrayTest, Resize_MoveOnly) { + ASSERT_EQ(0u, MoveOnlyType::num_instances()); + mojo::Array<MoveOnlyType> array(3); + std::vector<MoveOnlyType*> value_ptrs; + value_ptrs.push_back(array[0].ptr()); + value_ptrs.push_back(array[1].ptr()); + + for (size_t i = 0; i < array.size(); i++) + EXPECT_FALSE(array[i].moved()); + + array.resize(2); + ASSERT_EQ(2u, array.size()); + EXPECT_EQ(array.size(), MoveOnlyType::num_instances()); + for (size_t i = 0; i < array.size(); i++) { + EXPECT_FALSE(array[i].moved()); + EXPECT_EQ(value_ptrs[i], array[i].ptr()); + } + + array.resize(3); + ASSERT_EQ(3u, array.size()); + EXPECT_EQ(array.size(), MoveOnlyType::num_instances()); + for (size_t i = 0; i < array.size(); i++) + EXPECT_FALSE(array[i].moved()); + value_ptrs.push_back(array[2].ptr()); + + size_t capacity = array.storage().capacity(); + array.resize(capacity); + ASSERT_EQ(capacity, array.size()); + EXPECT_EQ(array.size(), MoveOnlyType::num_instances()); + for (size_t i = 0; i < array.size(); i++) + EXPECT_FALSE(array[i].moved()); + for (size_t i = 3; i < array.size(); i++) + value_ptrs.push_back(array[i].ptr()); + + array.resize(capacity + 2); + ASSERT_EQ(capacity + 2, array.size()); + EXPECT_EQ(array.size(), MoveOnlyType::num_instances()); + for (size_t i = 0; i < capacity; i++) { + EXPECT_TRUE(array[i].moved()); + EXPECT_EQ(value_ptrs[i], array[i].ptr()); + } + for (size_t i = capacity; i < array.size(); i++) + EXPECT_FALSE(array[i].moved()); + + array.reset(); + EXPECT_EQ(0u, MoveOnlyType::num_instances()); + EXPECT_FALSE(array); + array.resize(0); + EXPECT_EQ(0u, MoveOnlyType::num_instances()); + EXPECT_TRUE(array); +} + +TEST_F(ArrayTest, PushBack_Copyable) { + ASSERT_EQ(0u, CopyableType::num_instances()); + mojo::Array<CopyableType> array(2); + array.reset(); + std::vector<CopyableType*> value_ptrs; + size_t capacity = array.storage().capacity(); + for (size_t i = 0; i < capacity; i++) { + CopyableType value; + value_ptrs.push_back(value.ptr()); + array.push_back(value); + ASSERT_EQ(i + 1, array.size()); + ASSERT_EQ(i + 1, value_ptrs.size()); + EXPECT_EQ(array.size() + 1, CopyableType::num_instances()); + EXPECT_TRUE(array[i].copied()); + EXPECT_EQ(value_ptrs[i], array[i].ptr()); + array[i].ResetCopied(); + EXPECT_TRUE(array); + } + { + CopyableType value; + value_ptrs.push_back(value.ptr()); + array.push_back(value); + EXPECT_EQ(array.size() + 1, CopyableType::num_instances()); + } + ASSERT_EQ(capacity + 1, array.size()); + EXPECT_EQ(array.size(), CopyableType::num_instances()); + + for (size_t i = 0; i < array.size(); i++) { + EXPECT_TRUE(array[i].copied()); + EXPECT_EQ(value_ptrs[i], array[i].ptr()); + } + array.reset(); + EXPECT_EQ(0u, CopyableType::num_instances()); +} + +TEST_F(ArrayTest, PushBack_MoveOnly) { + ASSERT_EQ(0u, MoveOnlyType::num_instances()); + mojo::Array<MoveOnlyType> array(2); + array.reset(); + std::vector<MoveOnlyType*> value_ptrs; + size_t capacity = array.storage().capacity(); + for (size_t i = 0; i < capacity; i++) { + MoveOnlyType value; + value_ptrs.push_back(value.ptr()); + array.push_back(value.Pass()); + ASSERT_EQ(i + 1, array.size()); + ASSERT_EQ(i + 1, value_ptrs.size()); + EXPECT_EQ(array.size() + 1, MoveOnlyType::num_instances()); + EXPECT_TRUE(array[i].moved()); + EXPECT_EQ(value_ptrs[i], array[i].ptr()); + array[i].ResetMoved(); + EXPECT_TRUE(array); + } + { + MoveOnlyType value; + value_ptrs.push_back(value.ptr()); + array.push_back(value.Pass()); + EXPECT_EQ(array.size() + 1, MoveOnlyType::num_instances()); + } + ASSERT_EQ(capacity + 1, array.size()); + EXPECT_EQ(array.size(), MoveOnlyType::num_instances()); + + for (size_t i = 0; i < array.size(); i++) { + EXPECT_TRUE(array[i].moved()); + EXPECT_EQ(value_ptrs[i], array[i].ptr()); + } + array.reset(); + EXPECT_EQ(0u, MoveOnlyType::num_instances()); +} + +} // namespace +} // namespace test +} // namespace mojo diff --git a/mojo/public/cpp/bindings/tests/binding_callback_unittest.cc b/mojo/public/cpp/bindings/tests/binding_callback_unittest.cc new file mode 100644 index 0000000..83d74f9 --- /dev/null +++ b/mojo/public/cpp/bindings/tests/binding_callback_unittest.cc @@ -0,0 +1,305 @@ +// Copyright 2015 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. + +#include "base/message_loop/message_loop.h" +#include "build/build_config.h" +#include "mojo/message_pump/message_pump_mojo.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/interface_ptr.h" +#include "mojo/public/cpp/bindings/string.h" +#include "mojo/public/cpp/system/message_pipe.h" +#include "mojo/public/cpp/test_support/test_support.h" +#include "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" + +/////////////////////////////////////////////////////////////////////////////// +// +// The tests in this file are designed to test the interaction between a +// Callback and its associated Binding. If a Callback is deleted before +// being used we DCHECK fail--unless the associated Binding has already +// been closed or deleted. This contract must be explained to the Mojo +// application developer. For example it is the developer's responsibility to +// ensure that the Binding is destroyed before an unused Callback is destroyed. +// +/////////////////////////////////////////////////////////////////////////////// + +namespace mojo { +namespace test { +namespace { + +// A Runnable object that saves the last value it sees via the +// provided int32_t*. Used on the client side. +class ValueSaver { + public: + explicit ValueSaver(int32_t* last_value_seen) + : last_value_seen_(last_value_seen) {} + void Run(int32_t x) const { *last_value_seen_ = x; } + + private: + int32_t* const last_value_seen_; +}; + +// An implementation of sample::Provider used on the server side. +// It only implements one of the methods: EchoInt(). +// All it does is save the values and Callbacks it sees. +class InterfaceImpl : public sample::Provider { + public: + InterfaceImpl() + : last_server_value_seen_(0), + callback_saved_(new Callback<void(int32_t)>()) {} + + ~InterfaceImpl() override { + if (callback_saved_) { + delete callback_saved_; + } + } + + // Run's the callback previously saved from the last invocation + // of |EchoInt()|. + bool RunCallback() { + if (callback_saved_) { + callback_saved_->Run(last_server_value_seen_); + return true; + } + return false; + } + + // Delete's the previously saved callback. + void DeleteCallback() { + delete callback_saved_; + callback_saved_ = nullptr; + } + + // sample::Provider implementation + + // Saves its two input values in member variables and does nothing else. + void EchoInt(int32_t x, const Callback<void(int32_t)>& callback) override { + last_server_value_seen_ = x; + *callback_saved_ = callback; + } + + void EchoString(const String& a, + const Callback<void(String)>& callback) override { + MOJO_CHECK(false) << "Not implemented."; + } + + void EchoStrings(const String& a, + const String& b, + const Callback<void(String, String)>& callback) override { + MOJO_CHECK(false) << "Not implemented."; + } + + void EchoMessagePipeHandle( + ScopedMessagePipeHandle a, + const Callback<void(ScopedMessagePipeHandle)>& callback) override { + MOJO_CHECK(false) << "Not implemented."; + } + + void EchoEnum(sample::Enum a, + const Callback<void(sample::Enum)>& callback) override { + MOJO_CHECK(false) << "Not implemented."; + } + + void resetLastServerValueSeen() { last_server_value_seen_ = 0; } + + int32_t last_server_value_seen() const { return last_server_value_seen_; } + + private: + int32_t last_server_value_seen_; + Callback<void(int32_t)>* callback_saved_; +}; + +class BindingCallbackTest : public testing::Test { + public: + BindingCallbackTest() : loop_(common::MessagePumpMojo::Create()) {} + ~BindingCallbackTest() override {} + + protected: + int32_t last_client_callback_value_seen_; + sample::ProviderPtr interface_ptr_; + + void PumpMessages() { loop_.RunUntilIdle(); } + + private: + base::MessageLoop loop_; +}; + +// Tests that the InterfacePtr and the Binding can communicate with each +// other normally. +TEST_F(BindingCallbackTest, Basic) { + // Create the ServerImpl and the Binding. + InterfaceImpl server_impl; + Binding<sample::Provider> binding(&server_impl, GetProxy(&interface_ptr_)); + + // Initialize the test values. + server_impl.resetLastServerValueSeen(); + last_client_callback_value_seen_ = 0; + + // Invoke the Echo method. + interface_ptr_->EchoInt(7, ValueSaver(&last_client_callback_value_seen_)); + PumpMessages(); + + // Check that server saw the correct value, but the client has not yet. + EXPECT_EQ(7, server_impl.last_server_value_seen()); + EXPECT_EQ(0, last_client_callback_value_seen_); + + // Now run the Callback. + server_impl.RunCallback(); + PumpMessages(); + + // Check that the client has now seen the correct value. + EXPECT_EQ(7, last_client_callback_value_seen_); + + // Initialize the test values again. + server_impl.resetLastServerValueSeen(); + last_client_callback_value_seen_ = 0; + + // Invoke the Echo method again. + interface_ptr_->EchoInt(13, ValueSaver(&last_client_callback_value_seen_)); + PumpMessages(); + + // Check that server saw the correct value, but the client has not yet. + EXPECT_EQ(13, server_impl.last_server_value_seen()); + EXPECT_EQ(0, last_client_callback_value_seen_); + + // Now run the Callback again. + server_impl.RunCallback(); + PumpMessages(); + + // Check that the client has now seen the correct value again. + EXPECT_EQ(13, last_client_callback_value_seen_); +} + +// Tests that running the Callback after the Binding has been deleted +// results in a clean failure. +TEST_F(BindingCallbackTest, DeleteBindingThenRunCallback) { + // Create the ServerImpl. + InterfaceImpl server_impl; + { + // Create the binding in an inner scope so it can be deleted first. + Binding<sample::Provider> binding(&server_impl, GetProxy(&interface_ptr_)); + + // Initialize the test values. + server_impl.resetLastServerValueSeen(); + last_client_callback_value_seen_ = 0; + + // Invoke the Echo method. + interface_ptr_->EchoInt(7, ValueSaver(&last_client_callback_value_seen_)); + PumpMessages(); + } + // The binding has now been destroyed and the pipe is closed. + + // Check that server saw the correct value, but the client has not yet. + EXPECT_EQ(7, server_impl.last_server_value_seen()); + EXPECT_EQ(0, last_client_callback_value_seen_); + + // Now try to run the Callback. This should do nothing since the pipe + // is closed. + EXPECT_TRUE(server_impl.RunCallback()); + PumpMessages(); + + // Check that the client has still not seen the correct value. + EXPECT_EQ(0, last_client_callback_value_seen_); + + // Attempt to invoke the method again and confirm that an error was + // encountered. + interface_ptr_->EchoInt(13, ValueSaver(&last_client_callback_value_seen_)); + PumpMessages(); + EXPECT_TRUE(interface_ptr_.encountered_error()); +} + +// Tests that deleting a Callback without running it after the corresponding +// binding has already been deleted does not result in a crash. +TEST_F(BindingCallbackTest, DeleteBindingThenDeleteCallback) { + // Create the ServerImpl. + InterfaceImpl server_impl; + { + // Create the binding in an inner scope so it can be deleted first. + Binding<sample::Provider> binding(&server_impl, GetProxy(&interface_ptr_)); + + // Initialize the test values. + server_impl.resetLastServerValueSeen(); + last_client_callback_value_seen_ = 0; + + // Invoke the Echo method. + interface_ptr_->EchoInt(7, ValueSaver(&last_client_callback_value_seen_)); + PumpMessages(); + } + // The binding has now been destroyed and the pipe is closed. + + // Check that server saw the correct value, but the client has not yet. + EXPECT_EQ(7, server_impl.last_server_value_seen()); + EXPECT_EQ(0, last_client_callback_value_seen_); + + // Delete the callback without running it. This should not + // cause a problem because the insfrastructure can detect that the + // binding has already been destroyed and the pipe is closed. + server_impl.DeleteCallback(); +} + +// Tests that closing a Binding allows us to delete a callback +// without running it without encountering a crash. +TEST_F(BindingCallbackTest, CloseBindingBeforeDeletingCallback) { + // Create the ServerImpl and the Binding. + InterfaceImpl server_impl; + Binding<sample::Provider> binding(&server_impl, GetProxy(&interface_ptr_)); + + // Initialize the test values. + server_impl.resetLastServerValueSeen(); + last_client_callback_value_seen_ = 0; + + // Invoke the Echo method. + interface_ptr_->EchoInt(7, ValueSaver(&last_client_callback_value_seen_)); + PumpMessages(); + + // Check that server saw the correct value, but the client has not yet. + EXPECT_EQ(7, server_impl.last_server_value_seen()); + EXPECT_EQ(0, last_client_callback_value_seen_); + + // Now close the Binding. + binding.Close(); + + // Delete the callback without running it. This should not + // cause a crash because the insfrastructure can detect that the + // binding has already been closed. + server_impl.DeleteCallback(); + + // Check that the client has still not seen the correct value. + EXPECT_EQ(0, last_client_callback_value_seen_); +} + +// Tests that deleting a Callback without using it before the +// Binding has been destroyed or closed results in a DCHECK. +TEST_F(BindingCallbackTest, DeleteCallbackBeforeBindingDeathTest) { + // Create the ServerImpl and the Binding. + InterfaceImpl server_impl; + Binding<sample::Provider> binding(&server_impl, GetProxy(&interface_ptr_)); + + // Initialize the test values. + server_impl.resetLastServerValueSeen(); + last_client_callback_value_seen_ = 0; + + // Invoke the Echo method. + interface_ptr_->EchoInt(7, ValueSaver(&last_client_callback_value_seen_)); + PumpMessages(); + + // Check that server saw the correct value, but the client has not yet. + EXPECT_EQ(7, server_impl.last_server_value_seen()); + EXPECT_EQ(0, last_client_callback_value_seen_); + +#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON) + // Delete the callback without running it. This should cause a crash in debug + // builds due to a DCHECK. + std::string regex("Check failed: !callback_was_dropped."); +#if defined(OS_WIN) + // TODO(msw): Fix MOJO_DCHECK logs and EXPECT_DEATH* on Win: crbug.com/535014 + regex.clear(); +#endif // OS_WIN + EXPECT_DEATH_IF_SUPPORTED(server_impl.DeleteCallback(), regex.c_str()); +#endif // !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON) +} + +} // namespace +} // namespace test +} // namespace mojo diff --git a/mojo/public/cpp/bindings/tests/binding_unittest.cc b/mojo/public/cpp/bindings/tests/binding_unittest.cc new file mode 100644 index 0000000..1462b33 --- /dev/null +++ b/mojo/public/cpp/bindings/tests/binding_unittest.cc @@ -0,0 +1,364 @@ +// Copyright 2015 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. + +// Note: This file tests both binding.h (mojo::Binding) and strong_binding.h +// (mojo::StrongBinding). + +#include "base/message_loop/message_loop.h" +#include "mojo/message_pump/message_pump_mojo.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/strong_binding.h" +#include "mojo/public/cpp/system/macros.h" +#include "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom.h" +#include "mojo/public/interfaces/bindings/tests/sample_service.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace { + +class BindingTestBase : public testing::Test { + public: + BindingTestBase() : loop_(common::MessagePumpMojo::Create()) {} + ~BindingTestBase() override {} + + base::MessageLoop& loop() { return loop_; } + + private: + base::MessageLoop loop_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(BindingTestBase); +}; + +class ServiceImpl : public sample::Service { + public: + explicit ServiceImpl(bool* was_deleted = nullptr) + : was_deleted_(was_deleted) {} + ~ServiceImpl() override { + if (was_deleted_) + *was_deleted_ = true; + } + + private: + // sample::Service implementation + void Frobinate(sample::FooPtr foo, + BazOptions options, + sample::PortPtr port, + const FrobinateCallback& callback) override { + callback.Run(1); + } + void GetPort(InterfaceRequest<sample::Port> port) override {} + + bool* const was_deleted_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(ServiceImpl); +}; + +// BindingTest ----------------------------------------------------------------- + +using BindingTest = BindingTestBase; + +TEST_F(BindingTest, Close) { + bool called = false; + sample::ServicePtr ptr; + auto request = GetProxy(&ptr); + ptr.set_connection_error_handler([&called]() { called = true; }); + ServiceImpl impl; + Binding<sample::Service> binding(&impl, request.Pass()); + + binding.Close(); + EXPECT_FALSE(called); + loop().RunUntilIdle(); + EXPECT_TRUE(called); +} + +// Tests that destroying a mojo::Binding closes the bound message pipe handle. +TEST_F(BindingTest, DestroyClosesMessagePipe) { + bool encountered_error = false; + ServiceImpl impl; + sample::ServicePtr ptr; + auto request = GetProxy(&ptr); + ptr.set_connection_error_handler( + [&encountered_error]() { encountered_error = true; }); + bool called = false; + auto called_cb = [&called](int32_t result) { called = true; }; + { + Binding<sample::Service> binding(&impl, request.Pass()); + ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr, + called_cb); + loop().RunUntilIdle(); + EXPECT_TRUE(called); + EXPECT_FALSE(encountered_error); + } + // Now that the Binding is out of scope we should detect an error on the other + // end of the pipe. + loop().RunUntilIdle(); + EXPECT_TRUE(encountered_error); + + // And calls should fail. + called = false; + ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr, + called_cb); + loop().RunUntilIdle(); + EXPECT_FALSE(called); +} + +// Tests that the binding's connection error handler gets called when the other +// end is closed. +TEST_F(BindingTest, ConnectionError) { + bool called = false; + { + ServiceImpl impl; + sample::ServicePtr ptr; + Binding<sample::Service> binding(&impl, GetProxy(&ptr)); + binding.set_connection_error_handler([&called]() { called = true; }); + ptr.reset(); + EXPECT_FALSE(called); + loop().RunUntilIdle(); + EXPECT_TRUE(called); + // We want to make sure that it isn't called again during destruction. + called = false; + } + EXPECT_FALSE(called); +} + +// Tests that calling Close doesn't result in the connection error handler being +// called. +TEST_F(BindingTest, CloseDoesntCallConnectionErrorHandler) { + ServiceImpl impl; + sample::ServicePtr ptr; + Binding<sample::Service> binding(&impl, GetProxy(&ptr)); + bool called = false; + binding.set_connection_error_handler([&called]() { called = true; }); + binding.Close(); + loop().RunUntilIdle(); + EXPECT_FALSE(called); + + // We can also close the other end, and the error handler still won't be + // called. + ptr.reset(); + loop().RunUntilIdle(); + EXPECT_FALSE(called); +} + +class ServiceImplWithBinding : public ServiceImpl { + public: + ServiceImplWithBinding(bool* was_deleted, + InterfaceRequest<sample::Service> request) + : ServiceImpl(was_deleted), binding_(this, request.Pass()) { + binding_.set_connection_error_handler([this]() { delete this; }); + } + + private: + Binding<sample::Service> binding_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(ServiceImplWithBinding); +}; + +// Tests that the binding may be deleted in the connection error handler. +TEST_F(BindingTest, SelfDeleteOnConnectionError) { + bool was_deleted = false; + sample::ServicePtr ptr; + // This should delete itself on connection error. + new ServiceImplWithBinding(&was_deleted, GetProxy(&ptr)); + ptr.reset(); + EXPECT_FALSE(was_deleted); + loop().RunUntilIdle(); + EXPECT_TRUE(was_deleted); +} + +// Tests that explicitly calling Unbind followed by rebinding works. +TEST_F(BindingTest, Unbind) { + ServiceImpl impl; + sample::ServicePtr ptr; + Binding<sample::Service> binding(&impl, GetProxy(&ptr)); + + bool called = false; + auto called_cb = [&called](int32_t result) { called = true; }; + ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr, + called_cb); + loop().RunUntilIdle(); + EXPECT_TRUE(called); + + called = false; + auto request = binding.Unbind(); + EXPECT_FALSE(binding.is_bound()); + // All calls should fail when not bound... + ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr, + called_cb); + loop().RunUntilIdle(); + EXPECT_FALSE(called); + + called = false; + binding.Bind(request.Pass()); + EXPECT_TRUE(binding.is_bound()); + // ...and should succeed again when the rebound. + ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr, + called_cb); + loop().RunUntilIdle(); + EXPECT_TRUE(called); +} + +class IntegerAccessorImpl : public sample::IntegerAccessor { + public: + IntegerAccessorImpl() {} + ~IntegerAccessorImpl() override {} + + private: + // sample::IntegerAccessor implementation. + void GetInteger(const GetIntegerCallback& callback) override { + callback.Run(1, sample::ENUM_VALUE); + } + void SetInteger(int64_t data, sample::Enum type) override {} + + MOJO_DISALLOW_COPY_AND_ASSIGN(IntegerAccessorImpl); +}; + +TEST_F(BindingTest, SetInterfacePtrVersion) { + IntegerAccessorImpl impl; + sample::IntegerAccessorPtr ptr; + Binding<sample::IntegerAccessor> binding(&impl, &ptr); + EXPECT_EQ(3u, ptr.version()); +} + +TEST_F(BindingTest, PauseResume) { + bool called = false; + auto called_cb = [&called](int32_t result) { called = true; }; + sample::ServicePtr ptr; + auto request = GetProxy(&ptr); + ServiceImpl impl; + Binding<sample::Service> binding(&impl, request.Pass()); + binding.PauseIncomingMethodCallProcessing(); + ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr, + called_cb); + EXPECT_FALSE(called); + loop().RunUntilIdle(); + // Frobinate() should not be called as the binding is paused. + EXPECT_FALSE(called); + + // Resume the binding, which should trigger processing. + binding.ResumeIncomingMethodCallProcessing(); + loop().RunUntilIdle(); + EXPECT_TRUE(called); +} + +// Verifies the connection error handler is not run while a binding is paused. +TEST_F(BindingTest, ErrorHandleNotRunWhilePaused) { + bool called = false; + sample::ServicePtr ptr; + auto request = GetProxy(&ptr); + ServiceImpl impl; + Binding<sample::Service> binding(&impl, request.Pass()); + binding.set_connection_error_handler([&called]() { called = true; }); + binding.PauseIncomingMethodCallProcessing(); + + ptr.reset(); + loop().RunUntilIdle(); + // The connection error handle should not be called as the binding is paused. + EXPECT_FALSE(called); + + // Resume the binding, which should trigger the error handler. + binding.ResumeIncomingMethodCallProcessing(); + loop().RunUntilIdle(); + EXPECT_TRUE(called); +} + +// StrongBindingTest ----------------------------------------------------------- + +using StrongBindingTest = BindingTestBase; + +// Tests that destroying a mojo::StrongBinding closes the bound message pipe +// handle but does *not* destroy the implementation object. +TEST_F(StrongBindingTest, DestroyClosesMessagePipe) { + bool encountered_error = false; + bool was_deleted = false; + ServiceImpl impl(&was_deleted); + sample::ServicePtr ptr; + auto request = GetProxy(&ptr); + ptr.set_connection_error_handler( + [&encountered_error]() { encountered_error = true; }); + bool called = false; + auto called_cb = [&called](int32_t result) { called = true; }; + { + StrongBinding<sample::Service> binding(&impl, request.Pass()); + ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr, + called_cb); + loop().RunUntilIdle(); + EXPECT_TRUE(called); + EXPECT_FALSE(encountered_error); + } + // Now that the StrongBinding is out of scope we should detect an error on the + // other end of the pipe. + loop().RunUntilIdle(); + EXPECT_TRUE(encountered_error); + // But destroying the StrongBinding doesn't destroy the object. + ASSERT_FALSE(was_deleted); +} + +class ServiceImplWithStrongBinding : public ServiceImpl { + public: + ServiceImplWithStrongBinding(bool* was_deleted, + InterfaceRequest<sample::Service> request) + : ServiceImpl(was_deleted), binding_(this, request.Pass()) {} + + StrongBinding<sample::Service>& binding() { return binding_; } + + private: + StrongBinding<sample::Service> binding_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(ServiceImplWithStrongBinding); +}; + +// Tests the typical case, where the implementation object owns the +// StrongBinding (and should be destroyed on connection error). +TEST_F(StrongBindingTest, ConnectionErrorDestroysImpl) { + sample::ServicePtr ptr; + bool was_deleted = false; + // Will delete itself. + new ServiceImplWithBinding(&was_deleted, GetProxy(&ptr)); + + loop().RunUntilIdle(); + EXPECT_FALSE(was_deleted); + + ptr.reset(); + EXPECT_FALSE(was_deleted); + loop().RunUntilIdle(); + EXPECT_TRUE(was_deleted); +} + +// Tests that even when the implementation object owns the StrongBinding, that +// the implementation can still be deleted (which should result in the message +// pipe being closed). Also checks that the connection error handler doesn't get +// called. +TEST_F(StrongBindingTest, ExplicitDeleteImpl) { + bool ptr_error_handler_called = false; + sample::ServicePtr ptr; + auto request = GetProxy(&ptr); + ptr.set_connection_error_handler( + [&ptr_error_handler_called]() { ptr_error_handler_called = true; }); + bool was_deleted = false; + ServiceImplWithStrongBinding* impl = + new ServiceImplWithStrongBinding(&was_deleted, request.Pass()); + bool binding_error_handler_called = false; + impl->binding().set_connection_error_handler( + [&binding_error_handler_called]() { + binding_error_handler_called = true; + }); + + loop().RunUntilIdle(); + EXPECT_FALSE(ptr_error_handler_called); + EXPECT_FALSE(was_deleted); + + delete impl; + EXPECT_FALSE(ptr_error_handler_called); + EXPECT_TRUE(was_deleted); + was_deleted = false; // It shouldn't be double-deleted! + loop().RunUntilIdle(); + EXPECT_TRUE(ptr_error_handler_called); + EXPECT_FALSE(was_deleted); + + EXPECT_FALSE(binding_error_handler_called); +} + +} // namespace +} // mojo diff --git a/mojo/public/cpp/bindings/tests/bindings_perftest.cc b/mojo/public/cpp/bindings/tests/bindings_perftest.cc new file mode 100644 index 0000000..7440acc --- /dev/null +++ b/mojo/public/cpp/bindings/tests/bindings_perftest.cc @@ -0,0 +1,127 @@ +// Copyright 2015 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. + +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/test_support/test_support.h" +#include "mojo/public/cpp/test_support/test_utils.h" +#include "mojo/public/cpp/utility/run_loop.h" +#include "mojo/public/interfaces/bindings/tests/ping_service.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace { + +const double kMojoTicksPerSecond = 1000000.0; + +double MojoTicksToSeconds(MojoTimeTicks ticks) { + return ticks / kMojoTicksPerSecond; +} + +class PingServiceImpl : public test::PingService { + public: + explicit PingServiceImpl() {} + ~PingServiceImpl() override {} + + // |PingService| methods: + void Ping(const Callback<void()>& callback) override; + + private: + MOJO_DISALLOW_COPY_AND_ASSIGN(PingServiceImpl); +}; + +void PingServiceImpl::Ping(const Callback<void()>& callback) { + callback.Run(); +} + +class PingPongTest { + public: + explicit PingPongTest(test::PingServicePtr service); + + void Run(unsigned int iterations); + + private: + void OnPingDone(); + + test::PingServicePtr service_; + unsigned int iterations_to_run_; + unsigned int current_iterations_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(PingPongTest); +}; + +PingPongTest::PingPongTest(test::PingServicePtr service) + : service_(service.Pass()) { +} + +void PingPongTest::Run(unsigned int iterations) { + iterations_to_run_ = iterations; + current_iterations_ = 0; + + service_->Ping([this]() { OnPingDone(); }); + RunLoop::current()->Run(); +} + +void PingPongTest::OnPingDone() { + current_iterations_++; + if (current_iterations_ >= iterations_to_run_) { + RunLoop::current()->Quit(); + return; + } + + service_->Ping([this]() { OnPingDone(); }); +} + +struct BoundPingService { + BoundPingService() : binding(&impl) { + binding.Bind(GetProxy(&service)); + } + + PingServiceImpl impl; + test::PingServicePtr service; + Binding<test::PingService> binding; +}; + +class MojoBindingsPerftest : public testing::Test { + protected: + Environment env_; + RunLoop run_loop_; +}; + +TEST_F(MojoBindingsPerftest, InProcessPingPong) { + test::PingServicePtr service; + PingServiceImpl impl; + Binding<test::PingService> binding(&impl, GetProxy(&service)); + PingPongTest test(service.Pass()); + + { + const unsigned int kIterations = 100000; + const MojoTimeTicks start_time = MojoGetTimeTicksNow(); + test.Run(kIterations); + const MojoTimeTicks end_time = MojoGetTimeTicksNow(); + test::LogPerfResult( + "InProcessPingPong", "0_Inactive", + kIterations / MojoTicksToSeconds(end_time - start_time), + "pings/second"); + } + + { + const size_t kNumInactiveServices = 1000; + BoundPingService* inactive_services = + new BoundPingService[kNumInactiveServices]; + + const unsigned int kIterations = 10000; + const MojoTimeTicks start_time = MojoGetTimeTicksNow(); + test.Run(kIterations); + const MojoTimeTicks end_time = MojoGetTimeTicksNow(); + test::LogPerfResult( + "InProcessPingPong", "1000_Inactive", + kIterations / MojoTicksToSeconds(end_time - start_time), + "pings/second"); + + delete[] inactive_services; + } +} + +} // namespace +} // namespace mojo diff --git a/mojo/public/cpp/bindings/tests/bounds_checker_unittest.cc b/mojo/public/cpp/bindings/tests/bounds_checker_unittest.cc new file mode 100644 index 0000000..c1ec7b3 --- /dev/null +++ b/mojo/public/cpp/bindings/tests/bounds_checker_unittest.cc @@ -0,0 +1,209 @@ +// 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. + +#include <limits> + +#include "mojo/public/cpp/bindings/lib/bindings_serialization.h" +#include "mojo/public/cpp/bindings/lib/bounds_checker.h" +#include "mojo/public/cpp/system/core.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace test { +namespace { + +const void* ToPtr(uintptr_t ptr) { + return reinterpret_cast<const void*>(ptr); +} + +#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON) +TEST(BoundsCheckerTest, ConstructorRangeOverflow) { + { + // Test memory range overflow. + internal::BoundsChecker checker( + ToPtr(std::numeric_limits<uintptr_t>::max() - 3000), 5000, 0); + + EXPECT_FALSE(checker.IsValidRange( + ToPtr(std::numeric_limits<uintptr_t>::max() - 3000), 1)); + EXPECT_FALSE(checker.ClaimMemory( + ToPtr(std::numeric_limits<uintptr_t>::max() - 3000), 1)); + } + + if (sizeof(size_t) > sizeof(uint32_t)) { + // Test handle index range overflow. + size_t num_handles = + static_cast<size_t>(std::numeric_limits<uint32_t>::max()) + 5; + internal::BoundsChecker checker(ToPtr(0), 0, num_handles); + + EXPECT_FALSE(checker.ClaimHandle(Handle(0))); + EXPECT_FALSE( + checker.ClaimHandle(Handle(std::numeric_limits<uint32_t>::max() - 1))); + + EXPECT_TRUE( + checker.ClaimHandle(Handle(internal::kEncodedInvalidHandleValue))); + } +} +#endif + +TEST(BoundsCheckerTest, IsValidRange) { + { + internal::BoundsChecker checker(ToPtr(1234), 100, 0); + + // Basics. + EXPECT_FALSE(checker.IsValidRange(ToPtr(100), 5)); + EXPECT_FALSE(checker.IsValidRange(ToPtr(1230), 50)); + EXPECT_TRUE(checker.IsValidRange(ToPtr(1234), 5)); + EXPECT_TRUE(checker.IsValidRange(ToPtr(1240), 50)); + EXPECT_TRUE(checker.IsValidRange(ToPtr(1234), 100)); + EXPECT_FALSE(checker.IsValidRange(ToPtr(1234), 101)); + EXPECT_FALSE(checker.IsValidRange(ToPtr(1240), 100)); + EXPECT_FALSE(checker.IsValidRange(ToPtr(1333), 5)); + EXPECT_FALSE(checker.IsValidRange(ToPtr(2234), 5)); + + // ClaimMemory() updates the valid range. + EXPECT_TRUE(checker.ClaimMemory(ToPtr(1254), 10)); + + EXPECT_FALSE(checker.IsValidRange(ToPtr(1234), 1)); + EXPECT_FALSE(checker.IsValidRange(ToPtr(1254), 10)); + EXPECT_FALSE(checker.IsValidRange(ToPtr(1263), 1)); + EXPECT_FALSE(checker.IsValidRange(ToPtr(1263), 10)); + EXPECT_TRUE(checker.IsValidRange(ToPtr(1264), 10)); + EXPECT_TRUE(checker.IsValidRange(ToPtr(1264), 70)); + EXPECT_FALSE(checker.IsValidRange(ToPtr(1264), 71)); + } + + { + internal::BoundsChecker checker(ToPtr(1234), 100, 0); + // Should return false for empty ranges. + EXPECT_FALSE(checker.IsValidRange(ToPtr(0), 0)); + EXPECT_FALSE(checker.IsValidRange(ToPtr(1200), 0)); + EXPECT_FALSE(checker.IsValidRange(ToPtr(1234), 0)); + EXPECT_FALSE(checker.IsValidRange(ToPtr(1240), 0)); + EXPECT_FALSE(checker.IsValidRange(ToPtr(2234), 0)); + } + + { + // The valid memory range is empty. + internal::BoundsChecker checker(ToPtr(1234), 0, 0); + + EXPECT_FALSE(checker.IsValidRange(ToPtr(1234), 1)); + EXPECT_FALSE(checker.IsValidRange(ToPtr(1234), 0)); + } + + { + internal::BoundsChecker checker( + ToPtr(std::numeric_limits<uintptr_t>::max() - 2000), 1000, 0); + + // Test overflow. + EXPECT_FALSE(checker.IsValidRange( + ToPtr(std::numeric_limits<uintptr_t>::max() - 1500), 4000)); + EXPECT_FALSE(checker.IsValidRange( + ToPtr(std::numeric_limits<uintptr_t>::max() - 1500), + std::numeric_limits<uint32_t>::max())); + + // This should be fine. + EXPECT_TRUE(checker.IsValidRange( + ToPtr(std::numeric_limits<uintptr_t>::max() - 1500), 200)); + } +} + +TEST(BoundsCheckerTest, ClaimHandle) { + { + internal::BoundsChecker checker(ToPtr(0), 0, 10); + + // Basics. + EXPECT_TRUE(checker.ClaimHandle(Handle(0))); + EXPECT_FALSE(checker.ClaimHandle(Handle(0))); + + EXPECT_TRUE(checker.ClaimHandle(Handle(9))); + EXPECT_FALSE(checker.ClaimHandle(Handle(10))); + + // Should fail because it is smaller than the max index that has been + // claimed. + EXPECT_FALSE(checker.ClaimHandle(Handle(8))); + + // Should return true for invalid handle. + EXPECT_TRUE( + checker.ClaimHandle(Handle(internal::kEncodedInvalidHandleValue))); + EXPECT_TRUE( + checker.ClaimHandle(Handle(internal::kEncodedInvalidHandleValue))); + } + + { + // No handle to claim. + internal::BoundsChecker checker(ToPtr(0), 0, 0); + + EXPECT_FALSE(checker.ClaimHandle(Handle(0))); + + // Should still return true for invalid handle. + EXPECT_TRUE( + checker.ClaimHandle(Handle(internal::kEncodedInvalidHandleValue))); + } + + { + // Test the case that |num_handles| is the same value as + // |internal::kEncodedInvalidHandleValue|. + EXPECT_EQ(internal::kEncodedInvalidHandleValue, + std::numeric_limits<uint32_t>::max()); + internal::BoundsChecker checker( + ToPtr(0), 0, std::numeric_limits<uint32_t>::max()); + + EXPECT_TRUE( + checker.ClaimHandle(Handle(std::numeric_limits<uint32_t>::max() - 1))); + EXPECT_FALSE( + checker.ClaimHandle(Handle(std::numeric_limits<uint32_t>::max() - 1))); + EXPECT_FALSE(checker.ClaimHandle(Handle(0))); + + // Should still return true for invalid handle. + EXPECT_TRUE( + checker.ClaimHandle(Handle(internal::kEncodedInvalidHandleValue))); + } +} + +TEST(BoundsCheckerTest, ClaimMemory) { + { + internal::BoundsChecker checker(ToPtr(1000), 2000, 0); + + // Basics. + EXPECT_FALSE(checker.ClaimMemory(ToPtr(500), 100)); + EXPECT_FALSE(checker.ClaimMemory(ToPtr(800), 300)); + EXPECT_TRUE(checker.ClaimMemory(ToPtr(1000), 100)); + EXPECT_FALSE(checker.ClaimMemory(ToPtr(1099), 100)); + EXPECT_TRUE(checker.ClaimMemory(ToPtr(1100), 200)); + EXPECT_FALSE(checker.ClaimMemory(ToPtr(2000), 1001)); + EXPECT_TRUE(checker.ClaimMemory(ToPtr(2000), 500)); + EXPECT_FALSE(checker.ClaimMemory(ToPtr(2000), 500)); + EXPECT_FALSE(checker.ClaimMemory(ToPtr(1400), 100)); + EXPECT_FALSE(checker.ClaimMemory(ToPtr(3000), 1)); + EXPECT_TRUE(checker.ClaimMemory(ToPtr(2500), 500)); + } + + { + // No memory to claim. + internal::BoundsChecker checker(ToPtr(10000), 0, 0); + + EXPECT_FALSE(checker.ClaimMemory(ToPtr(10000), 1)); + EXPECT_FALSE(checker.ClaimMemory(ToPtr(10000), 0)); + } + + { + internal::BoundsChecker checker( + ToPtr(std::numeric_limits<uintptr_t>::max() - 1000), 500, 0); + + // Test overflow. + EXPECT_FALSE(checker.ClaimMemory( + ToPtr(std::numeric_limits<uintptr_t>::max() - 750), 4000)); + EXPECT_FALSE( + checker.ClaimMemory(ToPtr(std::numeric_limits<uintptr_t>::max() - 750), + std::numeric_limits<uint32_t>::max())); + + // This should be fine. + EXPECT_TRUE(checker.ClaimMemory( + ToPtr(std::numeric_limits<uintptr_t>::max() - 750), 200)); + } +} + +} // namespace +} // namespace test +} // namespace mojo diff --git a/mojo/public/cpp/bindings/tests/buffer_unittest.cc b/mojo/public/cpp/bindings/tests/buffer_unittest.cc new file mode 100644 index 0000000..317a2a5 --- /dev/null +++ b/mojo/public/cpp/bindings/tests/buffer_unittest.cc @@ -0,0 +1,91 @@ +// 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. + +#include <limits> + +#include "mojo/public/cpp/bindings/lib/bindings_serialization.h" +#include "mojo/public/cpp/bindings/lib/fixed_buffer.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace test { +namespace { + +bool IsZero(void* p_buf, size_t size) { + char* buf = reinterpret_cast<char*>(p_buf); + for (size_t i = 0; i < size; ++i) { + if (buf[i] != 0) + return false; + } + return true; +} + +// Tests that FixedBuffer allocates memory aligned to 8 byte boundaries. +TEST(FixedBufferTest, Alignment) { + internal::FixedBufferForTesting buf(internal::Align(10) * 2); + ASSERT_EQ(buf.size(), 16u * 2); + + void* a = buf.Allocate(10); + ASSERT_TRUE(a); + EXPECT_TRUE(IsZero(a, 10)); + EXPECT_EQ(0, reinterpret_cast<ptrdiff_t>(a) % 8); + + void* b = buf.Allocate(10); + ASSERT_TRUE(b); + EXPECT_TRUE(IsZero(b, 10)); + EXPECT_EQ(0, reinterpret_cast<ptrdiff_t>(b) % 8); + + // Any more allocations would result in an assert, but we can't test that. +} + +// Tests that FixedBufferForTesting::Leak passes ownership to the caller. +TEST(FixedBufferTest, Leak) { + void* ptr = nullptr; + void* buf_ptr = nullptr; + { + internal::FixedBufferForTesting buf(8); + ASSERT_EQ(8u, buf.size()); + + ptr = buf.Allocate(8); + ASSERT_TRUE(ptr); + buf_ptr = buf.Leak(); + + // The buffer should point to the first element allocated. + // TODO(mpcomplete): Is this a reasonable expectation? + EXPECT_EQ(ptr, buf_ptr); + + // The FixedBufferForTesting should be empty now. + EXPECT_EQ(0u, buf.size()); + EXPECT_FALSE(buf.Leak()); + } + + // Since we called Leak, ptr is still writable after FixedBufferForTesting + // went out of scope. + memset(ptr, 1, 8); + free(buf_ptr); +} + +#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON) +TEST(FixedBufferTest, TooBig) { + internal::FixedBufferForTesting buf(24); + + // A little bit too large. + EXPECT_EQ(reinterpret_cast<void*>(0), buf.Allocate(32)); + + // Move the cursor forward. + EXPECT_NE(reinterpret_cast<void*>(0), buf.Allocate(16)); + + // A lot too large. + EXPECT_EQ(reinterpret_cast<void*>(0), + buf.Allocate(std::numeric_limits<size_t>::max() - 1024u)); + + // A lot too large, leading to possible integer overflow. + EXPECT_EQ(reinterpret_cast<void*>(0), + buf.Allocate(std::numeric_limits<size_t>::max() - 8u)); +} +#endif + +} // namespace +} // namespace test +} // namespace mojo diff --git a/mojo/public/cpp/bindings/tests/callback_unittest.cc b/mojo/public/cpp/bindings/tests/callback_unittest.cc new file mode 100644 index 0000000..158b21e --- /dev/null +++ b/mojo/public/cpp/bindings/tests/callback_unittest.cc @@ -0,0 +1,196 @@ +// Copyright 2015 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. + +#include "mojo/public/cpp/bindings/callback.h" +#include "mojo/public/cpp/bindings/map.h" +#include "mojo/public/cpp/bindings/string.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace test { +namespace { + +struct RunnableNoArgs { + explicit RunnableNoArgs(int* calls) : calls(calls) {} + + void Run() const { (*calls)++; } + int* calls; +}; + +struct RunnableOneArg { + explicit RunnableOneArg(int* calls) : calls(calls) {} + + void Run(int increment) const { (*calls) += increment; } + + int* calls; +}; + +struct RunnableStringArgByConstRef { + explicit RunnableStringArgByConstRef(int* calls) : calls(calls) {} + + void Run(const String& s) const { (*calls)++; } + + int* calls; +}; + +using ExampleMoveOnlyType = Map<int, int>; + +struct RunnableMoveOnlyParam { + explicit RunnableMoveOnlyParam(int* calls) : calls(calls) {} + + void Run(ExampleMoveOnlyType m) const { (*calls)++; } + + int* calls; +}; + +int* g_calls = nullptr; + +void FunctionNoArgs() { + (*g_calls)++; +} + +void FunctionOneArg(int increment) { + (*g_calls) += increment; +} + +void FunctionStringArgByConstRef(const String& s) { + (*g_calls)++; +} + +void FunctionMoveOnlyType(ExampleMoveOnlyType m) { + (*g_calls)++; +} + +static_assert(!internal::HasCompatibleCallOperator<RunnableNoArgs>::value, + "HasCompatibleCallOperator<Runnable>"); +static_assert(!internal::HasCompatibleCallOperator<RunnableOneArg, int>::value, + "HasCompatibleCallOperator<RunnableOneArg, int>"); +static_assert(!internal::HasCompatibleCallOperator<RunnableStringArgByConstRef, + String>::value, + "HasCompatibleCallOperator<RunnableStringArgByConstRef, String>"); +static_assert(!internal::HasCompatibleCallOperator<RunnableMoveOnlyParam, + ExampleMoveOnlyType>::value, + "HasCompatibleCallOperator<RunnableMoveOnlyParam, String>"); + +auto lambda_one = []() {}; +static_assert(internal::HasCompatibleCallOperator<decltype(lambda_one)>::value, + "HasCompatibleCallOperator<lambda []() {}>"); + +auto lambda_two = [](int x) {}; +static_assert( + internal::HasCompatibleCallOperator<decltype(lambda_two), int>::value, + "HasCompatibleCallOperator<lambda [](int x) {}, int>"); + +auto lambda_three = [](const String& s) {}; +static_assert( + internal::HasCompatibleCallOperator<decltype(lambda_three), String>::value, + "HasCompatibleCallOperator<lambda [](const String& s) {}, String>"); + +auto lambda_four = [](ExampleMoveOnlyType m) {}; +static_assert(internal::HasCompatibleCallOperator<decltype(lambda_four), + ExampleMoveOnlyType>::value, + "HasCompatibleCallOperator<lambda [](ExampleMoveOnlyType) {}, " + "ExampleMoveOnlyType>"); + +// Tests constructing and invoking a mojo::Callback from objects with a +// compatible Run() method (called 'runnables'), from lambdas, and from function +// pointers. +TEST(Callback, Create) { + int calls = 0; + + RunnableNoArgs f(&calls); + // Construct from a runnable object. + mojo::Callback<void()> cb = f; + cb.Run(); + EXPECT_EQ(1, calls); + + // Construct from a parameterless lambda that captures one variable. + cb = [&calls]() { calls++; }; + cb.Run(); + EXPECT_EQ(2, calls); + + // Construct from a runnable object with one primitive parameter. + mojo::Callback<void(int)> cb_with_param = RunnableOneArg(&calls); + cb_with_param.Run(1); + EXPECT_EQ(3, calls); + + // Construct from a lambda that takes one parameter and captures one variable. + cb_with_param = [&calls](int increment) { calls += increment; }; + cb_with_param.Run(1); + EXPECT_EQ(4, calls); + + // Construct from a runnable object with one string parameter. + mojo::Callback<void(String)> cb_with_string_param = + RunnableStringArgByConstRef(&calls); + cb_with_string_param.Run(String("hello world")); + EXPECT_EQ(5, calls); + + // Construct from a lambda that takes one string parameter. + cb_with_string_param = [&calls](const String& s) { calls++; }; + cb_with_string_param.Run(String("world")); + EXPECT_EQ(6, calls); + + ExampleMoveOnlyType m; + mojo::Callback<void(ExampleMoveOnlyType)> cb_with_move_only_param = + RunnableMoveOnlyParam(&calls); + cb_with_move_only_param.Run(m.Clone()); + EXPECT_EQ(7, calls); + + cb_with_move_only_param = [&calls](ExampleMoveOnlyType m) { calls++; }; + cb_with_move_only_param.Run(m.Clone()); + EXPECT_EQ(8, calls); + + // Construct from a function pointer. + g_calls = &calls; + + cb = &FunctionNoArgs; + cb.Run(); + EXPECT_EQ(9, calls); + + cb_with_param = &FunctionOneArg; + cb_with_param.Run(1); + EXPECT_EQ(10, calls); + + cb_with_string_param = &FunctionStringArgByConstRef; + cb_with_string_param.Run(String("hello")); + EXPECT_EQ(11, calls); + + cb_with_move_only_param = &FunctionMoveOnlyType; + cb_with_move_only_param.Run(m.Clone()); + EXPECT_EQ(12, calls); + + g_calls = nullptr; +} + +bool g_overloaded_function_with_int_param_called = false; + +void OverloadedFunction(int param) { + g_overloaded_function_with_int_param_called = true; +} + +bool g_overloaded_function_with_double_param_called = false; + +void OverloadedFunction(double param) { + g_overloaded_function_with_double_param_called = true; +} + +// Tests constructing and invoking a mojo::Callback from pointers to overloaded +// functions. +TEST(Callback, CreateFromOverloadedFunctionPtr) { + g_overloaded_function_with_int_param_called = false; + mojo::Callback<void(int)> cb_with_int_param = &OverloadedFunction; + cb_with_int_param.Run(123); + EXPECT_TRUE(g_overloaded_function_with_int_param_called); + g_overloaded_function_with_int_param_called = false; + + g_overloaded_function_with_double_param_called = false; + mojo::Callback<void(double)> cb_with_double_param = &OverloadedFunction; + cb_with_double_param.Run(123); + EXPECT_TRUE(g_overloaded_function_with_double_param_called); + g_overloaded_function_with_double_param_called = false; +} + +} // namespace +} // namespace test +} // namespace mojo diff --git a/mojo/public/cpp/bindings/tests/connector_unittest.cc b/mojo/public/cpp/bindings/tests/connector_unittest.cc new file mode 100644 index 0000000..febc72e --- /dev/null +++ b/mojo/public/cpp/bindings/tests/connector_unittest.cc @@ -0,0 +1,443 @@ +// 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. + +#include <stdlib.h> +#include <string.h> + +#include "base/message_loop/message_loop.h" +#include "mojo/message_pump/message_pump_mojo.h" +#include "mojo/public/cpp/bindings/lib/connector.h" +#include "mojo/public/cpp/bindings/lib/message_builder.h" +#include "mojo/public/cpp/bindings/tests/message_queue.h" +#include "mojo/public/cpp/system/macros.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace test { +namespace { + +class MessageAccumulator : public MessageReceiver { + public: + MessageAccumulator() {} + + bool Accept(Message* message) override { + queue_.Push(message); + return true; + } + + bool IsEmpty() const { return queue_.IsEmpty(); } + + void Pop(Message* message) { queue_.Pop(message); } + + private: + MessageQueue queue_; +}; + +class ConnectorDeletingMessageAccumulator : public MessageAccumulator { + public: + ConnectorDeletingMessageAccumulator(internal::Connector** connector) + : connector_(connector) {} + + bool Accept(Message* message) override { + delete *connector_; + *connector_ = nullptr; + return MessageAccumulator::Accept(message); + } + + private: + internal::Connector** connector_; +}; + +class ReentrantMessageAccumulator : public MessageAccumulator { + public: + ReentrantMessageAccumulator(internal::Connector* connector) + : connector_(connector), number_of_calls_(0) {} + + bool Accept(Message* message) override { + if (!MessageAccumulator::Accept(message)) + return false; + number_of_calls_++; + if (number_of_calls_ == 1) { + return connector_->WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE); + } + return true; + } + + int number_of_calls() { return number_of_calls_; } + + private: + internal::Connector* connector_; + int number_of_calls_; +}; + +class ConnectorTest : public testing::Test { + public: + ConnectorTest() : loop_(common::MessagePumpMojo::Create()) {} + + void SetUp() override { + CreateMessagePipe(nullptr, &handle0_, &handle1_); + } + + void TearDown() override {} + + void AllocMessage(const char* text, Message* message) { + size_t payload_size = strlen(text) + 1; // Plus null terminator. + internal::MessageBuilder builder(1, payload_size); + memcpy(builder.buffer()->Allocate(payload_size), text, payload_size); + + builder.message()->MoveTo(message); + } + + void PumpMessages() { loop_.RunUntilIdle(); } + + protected: + ScopedMessagePipeHandle handle0_; + ScopedMessagePipeHandle handle1_; + + private: + base::MessageLoop loop_; +}; + +TEST_F(ConnectorTest, Basic) { + internal::Connector connector0(handle0_.Pass()); + internal::Connector connector1(handle1_.Pass()); + + const char kText[] = "hello world"; + + Message message; + AllocMessage(kText, &message); + + connector0.Accept(&message); + + MessageAccumulator accumulator; + connector1.set_incoming_receiver(&accumulator); + + PumpMessages(); + + ASSERT_FALSE(accumulator.IsEmpty()); + + Message message_received; + accumulator.Pop(&message_received); + + EXPECT_EQ( + std::string(kText), + std::string(reinterpret_cast<const char*>(message_received.payload()))); +} + +TEST_F(ConnectorTest, Basic_Synchronous) { + internal::Connector connector0(handle0_.Pass()); + internal::Connector connector1(handle1_.Pass()); + + const char kText[] = "hello world"; + + Message message; + AllocMessage(kText, &message); + + connector0.Accept(&message); + + MessageAccumulator accumulator; + connector1.set_incoming_receiver(&accumulator); + + connector1.WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE); + + ASSERT_FALSE(accumulator.IsEmpty()); + + Message message_received; + accumulator.Pop(&message_received); + + EXPECT_EQ( + std::string(kText), + std::string(reinterpret_cast<const char*>(message_received.payload()))); +} + +TEST_F(ConnectorTest, Basic_EarlyIncomingReceiver) { + internal::Connector connector0(handle0_.Pass()); + internal::Connector connector1(handle1_.Pass()); + + MessageAccumulator accumulator; + connector1.set_incoming_receiver(&accumulator); + + const char kText[] = "hello world"; + + Message message; + AllocMessage(kText, &message); + + connector0.Accept(&message); + + PumpMessages(); + + ASSERT_FALSE(accumulator.IsEmpty()); + + Message message_received; + accumulator.Pop(&message_received); + + EXPECT_EQ( + std::string(kText), + std::string(reinterpret_cast<const char*>(message_received.payload()))); +} + +TEST_F(ConnectorTest, Basic_TwoMessages) { + internal::Connector connector0(handle0_.Pass()); + internal::Connector connector1(handle1_.Pass()); + + const char* kText[] = {"hello", "world"}; + + for (size_t i = 0; i < MOJO_ARRAYSIZE(kText); ++i) { + Message message; + AllocMessage(kText[i], &message); + + connector0.Accept(&message); + } + + MessageAccumulator accumulator; + connector1.set_incoming_receiver(&accumulator); + + PumpMessages(); + + for (size_t i = 0; i < MOJO_ARRAYSIZE(kText); ++i) { + ASSERT_FALSE(accumulator.IsEmpty()); + + Message message_received; + accumulator.Pop(&message_received); + + EXPECT_EQ( + std::string(kText[i]), + std::string(reinterpret_cast<const char*>(message_received.payload()))); + } +} + +TEST_F(ConnectorTest, Basic_TwoMessages_Synchronous) { + internal::Connector connector0(handle0_.Pass()); + internal::Connector connector1(handle1_.Pass()); + + const char* kText[] = {"hello", "world"}; + + for (size_t i = 0; i < MOJO_ARRAYSIZE(kText); ++i) { + Message message; + AllocMessage(kText[i], &message); + + connector0.Accept(&message); + } + + MessageAccumulator accumulator; + connector1.set_incoming_receiver(&accumulator); + + connector1.WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE); + + ASSERT_FALSE(accumulator.IsEmpty()); + + Message message_received; + accumulator.Pop(&message_received); + + EXPECT_EQ( + std::string(kText[0]), + std::string(reinterpret_cast<const char*>(message_received.payload()))); + + ASSERT_TRUE(accumulator.IsEmpty()); +} + +TEST_F(ConnectorTest, WriteToClosedPipe) { + internal::Connector connector0(handle0_.Pass()); + + const char kText[] = "hello world"; + + Message message; + AllocMessage(kText, &message); + + // Close the other end of the pipe. + handle1_.reset(); + + // Not observed yet because we haven't spun the message loop yet. + EXPECT_FALSE(connector0.encountered_error()); + + // Write failures are not reported. + bool ok = connector0.Accept(&message); + EXPECT_TRUE(ok); + + // Still not observed. + EXPECT_FALSE(connector0.encountered_error()); + + // Spin the message loop, and then we should start observing the closed pipe. + PumpMessages(); + + EXPECT_TRUE(connector0.encountered_error()); +} + +TEST_F(ConnectorTest, MessageWithHandles) { + internal::Connector connector0(handle0_.Pass()); + internal::Connector connector1(handle1_.Pass()); + + const char kText[] = "hello world"; + + Message message1; + AllocMessage(kText, &message1); + + MessagePipe pipe; + message1.mutable_handles()->push_back(pipe.handle0.release()); + + connector0.Accept(&message1); + + // The message should have been transferred, releasing the handles. + EXPECT_TRUE(message1.handles()->empty()); + + MessageAccumulator accumulator; + connector1.set_incoming_receiver(&accumulator); + + PumpMessages(); + + ASSERT_FALSE(accumulator.IsEmpty()); + + Message message_received; + accumulator.Pop(&message_received); + + EXPECT_EQ( + std::string(kText), + std::string(reinterpret_cast<const char*>(message_received.payload()))); + ASSERT_EQ(1U, message_received.handles()->size()); + + // Now send a message to the transferred handle and confirm it's sent through + // to the orginal pipe. + // TODO(vtl): Do we need a better way of "downcasting" the handle types? + ScopedMessagePipeHandle smph; + smph.reset(MessagePipeHandle(message_received.handles()->front().value())); + message_received.mutable_handles()->front() = Handle(); + // |smph| now owns this handle. + + internal::Connector connector_received(smph.Pass()); + internal::Connector connector_original(pipe.handle1.Pass()); + + Message message2; + AllocMessage(kText, &message2); + + connector_received.Accept(&message2); + connector_original.set_incoming_receiver(&accumulator); + PumpMessages(); + + ASSERT_FALSE(accumulator.IsEmpty()); + + accumulator.Pop(&message_received); + + EXPECT_EQ( + std::string(kText), + std::string(reinterpret_cast<const char*>(message_received.payload()))); +} + +TEST_F(ConnectorTest, WaitForIncomingMessageWithError) { + internal::Connector connector0(handle0_.Pass()); + // Close the other end of the pipe. + handle1_.reset(); + ASSERT_FALSE(connector0.WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE)); +} + +TEST_F(ConnectorTest, WaitForIncomingMessageWithDeletion) { + internal::Connector connector0(handle0_.Pass()); + internal::Connector* connector1 = new internal::Connector(handle1_.Pass()); + + const char kText[] = "hello world"; + + Message message; + AllocMessage(kText, &message); + + connector0.Accept(&message); + + ConnectorDeletingMessageAccumulator accumulator(&connector1); + connector1->set_incoming_receiver(&accumulator); + + connector1->WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE); + + ASSERT_FALSE(connector1); + ASSERT_FALSE(accumulator.IsEmpty()); + + Message message_received; + accumulator.Pop(&message_received); + + EXPECT_EQ( + std::string(kText), + std::string(reinterpret_cast<const char*>(message_received.payload()))); +} + +TEST_F(ConnectorTest, WaitForIncomingMessageWithReentrancy) { + internal::Connector connector0(handle0_.Pass()); + internal::Connector connector1(handle1_.Pass()); + + const char* kText[] = {"hello", "world"}; + + for (size_t i = 0; i < MOJO_ARRAYSIZE(kText); ++i) { + Message message; + AllocMessage(kText[i], &message); + + connector0.Accept(&message); + } + + ReentrantMessageAccumulator accumulator(&connector1); + connector1.set_incoming_receiver(&accumulator); + + PumpMessages(); + + for (size_t i = 0; i < MOJO_ARRAYSIZE(kText); ++i) { + ASSERT_FALSE(accumulator.IsEmpty()); + + Message message_received; + accumulator.Pop(&message_received); + + EXPECT_EQ( + std::string(kText[i]), + std::string(reinterpret_cast<const char*>(message_received.payload()))); + } + + ASSERT_EQ(2, accumulator.number_of_calls()); +} + +TEST_F(ConnectorTest, RaiseError) { + internal::Connector connector0(handle0_.Pass()); + bool error_handler_called0 = false; + connector0.set_connection_error_handler( + [&error_handler_called0]() { error_handler_called0 = true; }); + + internal::Connector connector1(handle1_.Pass()); + bool error_handler_called1 = false; + connector1.set_connection_error_handler( + [&error_handler_called1]() { error_handler_called1 = true; }); + + const char kText[] = "hello world"; + + Message message; + AllocMessage(kText, &message); + + connector0.Accept(&message); + connector0.RaiseError(); + + MessageAccumulator accumulator; + connector1.set_incoming_receiver(&accumulator); + + PumpMessages(); + + // Messages sent prior to RaiseError() still arrive at the other end. + ASSERT_FALSE(accumulator.IsEmpty()); + + Message message_received; + accumulator.Pop(&message_received); + + EXPECT_EQ( + std::string(kText), + std::string(reinterpret_cast<const char*>(message_received.payload()))); + + PumpMessages(); + + // Connection error handler is called at both sides. + EXPECT_TRUE(error_handler_called0); + EXPECT_TRUE(error_handler_called1); + + // The error flag is set at both sides. + EXPECT_TRUE(connector0.encountered_error()); + EXPECT_TRUE(connector1.encountered_error()); + + // The message pipe handle is valid at both sides. + EXPECT_TRUE(connector0.is_valid()); + EXPECT_TRUE(connector1.is_valid()); +} + +} // namespace +} // namespace test +} // namespace mojo diff --git a/mojo/public/cpp/bindings/tests/constant_unittest.cc b/mojo/public/cpp/bindings/tests/constant_unittest.cc new file mode 100644 index 0000000..f6394f3 --- /dev/null +++ b/mojo/public/cpp/bindings/tests/constant_unittest.cc @@ -0,0 +1,42 @@ +// Copyright 2015 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. + +#include "mojo/public/interfaces/bindings/tests/test_constants.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace test { + +TEST(ConstantTest, GlobalConstants) { + // Compile-time constants. + static_assert(kBoolValue == true, ""); + static_assert(kInt8Value == -2, ""); + static_assert(kUint8Value == 128U, ""); + static_assert(kInt16Value == -233, ""); + static_assert(kUint16Value == 44204U, ""); + static_assert(kInt32Value == -44204, ""); + static_assert(kUint32Value == 4294967295U, ""); + static_assert(kInt64Value == -9223372036854775807, ""); + static_assert(kUint64Value == 9999999999999999999ULL, ""); + + EXPECT_DOUBLE_EQ(kDoubleValue, 3.14159); + EXPECT_FLOAT_EQ(kFloatValue, 2.71828f); +} + +TEST(ConstantTest, StructConstants) { + // Compile-time constants. + static_assert(StructWithConstants::kInt8Value == 5U, ""); + + EXPECT_FLOAT_EQ(StructWithConstants::kFloatValue, 765.432f); +} + +TEST(ConstantTest, InterfaceConstants) { + // Compile-time constants. + static_assert(InterfaceWithConstants::kUint32Value == 20100722, ""); + + EXPECT_DOUBLE_EQ(InterfaceWithConstants::kDoubleValue, 12.34567); +} + +} // namespace test +} // namespace mojo diff --git a/mojo/public/cpp/bindings/tests/container_test_util.cc b/mojo/public/cpp/bindings/tests/container_test_util.cc new file mode 100644 index 0000000..e8377c4 --- /dev/null +++ b/mojo/public/cpp/bindings/tests/container_test_util.cc @@ -0,0 +1,50 @@ +// 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. + +#include "mojo/public/cpp/bindings/tests/container_test_util.h" + +namespace mojo { + +size_t CopyableType::num_instances_ = 0; +size_t MoveOnlyType::num_instances_ = 0; + +CopyableType::CopyableType() : copied_(false), ptr_(this) { + num_instances_++; +} + +CopyableType::CopyableType(const CopyableType& other) + : copied_(true), ptr_(other.ptr()) { + num_instances_++; +} + +CopyableType& CopyableType::operator=(const CopyableType& other) { + copied_ = true; + ptr_ = other.ptr(); + return *this; +} + +CopyableType::~CopyableType() { + num_instances_--; +} + +MoveOnlyType::MoveOnlyType() : moved_(false), ptr_(this) { + num_instances_++; +} + +MoveOnlyType::MoveOnlyType(MoveOnlyType&& other) + : moved_(true), ptr_(other.ptr()) { + num_instances_++; +} + +MoveOnlyType& MoveOnlyType::operator=(MoveOnlyType&& other) { + moved_ = true; + ptr_ = other.ptr(); + return *this; +} + +MoveOnlyType::~MoveOnlyType() { + num_instances_--; +} + +} // namespace mojo diff --git a/mojo/public/cpp/bindings/tests/container_test_util.h b/mojo/public/cpp/bindings/tests/container_test_util.h new file mode 100644 index 0000000..1e29d22 --- /dev/null +++ b/mojo/public/cpp/bindings/tests/container_test_util.h @@ -0,0 +1,52 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_TESTS_CONTAINER_TEST_UTIL_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_TESTS_CONTAINER_TEST_UTIL_H_ + +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { + +class CopyableType { + public: + CopyableType(); + CopyableType(const CopyableType& other); + CopyableType& operator=(const CopyableType& other); + ~CopyableType(); + + bool copied() const { return copied_; } + static size_t num_instances() { return num_instances_; } + CopyableType* ptr() const { return ptr_; } + void ResetCopied() { copied_ = false; } + + private: + bool copied_; + static size_t num_instances_; + CopyableType* ptr_; +}; + +class MoveOnlyType { + MOJO_MOVE_ONLY_TYPE(MoveOnlyType) + public: + typedef MoveOnlyType Data_; + MoveOnlyType(); + MoveOnlyType(MoveOnlyType&& other); + MoveOnlyType& operator=(MoveOnlyType&& other); + ~MoveOnlyType(); + + bool moved() const { return moved_; } + static size_t num_instances() { return num_instances_; } + MoveOnlyType* ptr() const { return ptr_; } + void ResetMoved() { moved_ = false; } + + private: + bool moved_; + static size_t num_instances_; + MoveOnlyType* ptr_; +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_TESTS_CONTAINER_TEST_UTIL_H_ diff --git a/mojo/public/cpp/bindings/tests/equals_unittest.cc b/mojo/public/cpp/bindings/tests/equals_unittest.cc new file mode 100644 index 0000000..5306f1f --- /dev/null +++ b/mojo/public/cpp/bindings/tests/equals_unittest.cc @@ -0,0 +1,158 @@ +// 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. + +#include "mojo/public/cpp/bindings/lib/value_traits.h" +#include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace test { + +namespace { + +RectPtr CreateRect() { + RectPtr r = Rect::New(); + r->x = 1; + r->y = 2; + r->width = 3; + r->height = 4; + return r.Pass(); +} + +using EqualsTest = testing::Test; + +} // namespace + +TEST_F(EqualsTest, NullStruct) { + RectPtr r1; + RectPtr r2; + EXPECT_TRUE(r1.Equals(r2)); + EXPECT_TRUE(r2.Equals(r1)); + + r1 = CreateRect(); + EXPECT_FALSE(r1.Equals(r2)); + EXPECT_FALSE(r2.Equals(r1)); +} + +TEST_F(EqualsTest, Struct) { + RectPtr r1(CreateRect()); + RectPtr r2(r1.Clone()); + EXPECT_TRUE(r1.Equals(r2)); + r2->y = 1; + EXPECT_FALSE(r1.Equals(r2)); + r2.reset(); + EXPECT_FALSE(r1.Equals(r2)); +} + +TEST_F(EqualsTest, StructNested) { + RectPairPtr p1(RectPair::New()); + p1->first = CreateRect(); + p1->second = CreateRect(); + RectPairPtr p2(p1.Clone()); + EXPECT_TRUE(p1.Equals(p2)); + p2->second->width = 0; + EXPECT_FALSE(p1.Equals(p2)); + p2->second.reset(); + EXPECT_FALSE(p1.Equals(p2)); +} + +TEST_F(EqualsTest, Array) { + NamedRegionPtr n1(NamedRegion::New()); + n1->name = "n1"; + n1->rects.push_back(CreateRect()); + NamedRegionPtr n2(n1.Clone()); + EXPECT_TRUE(n1.Equals(n2)); + + n2->rects.reset(); + EXPECT_FALSE(n1.Equals(n2)); + n2->rects.resize(0); + EXPECT_FALSE(n1.Equals(n2)); + + n2->rects.push_back(CreateRect()); + n2->rects.push_back(CreateRect()); + EXPECT_FALSE(n1.Equals(n2)); + + n2->rects.resize(1); + n2->rects[0]->width = 0; + EXPECT_FALSE(n1.Equals(n2)); + + n2->rects[0] = CreateRect(); + EXPECT_TRUE(n1.Equals(n2)); +} + +TEST_F(EqualsTest, Map) { + auto n1(NamedRegion::New()); + n1->name = "foo"; + n1->rects.push_back(CreateRect()); + + Map<std::string, NamedRegionPtr> m1; + m1.insert("foo", n1.Pass()); + + decltype(m1) m2; + EXPECT_FALSE(m1.Equals(m2)); + + m2.insert("bar", m1.at("foo").Clone()); + EXPECT_FALSE(m1.Equals(m2)); + + m2 = m1.Clone(); + m2.at("foo")->name = "monkey"; + EXPECT_FALSE(m1.Equals(m2)); + + m2 = m1.Clone(); + m2.at("foo")->rects.push_back(Rect::New()); + EXPECT_FALSE(m1.Equals(m2)); + + m2.at("foo")->rects.resize(1); + m2.at("foo")->rects[0]->width = 1; + EXPECT_FALSE(m1.Equals(m2)); + + m2 = m1.Clone(); + EXPECT_TRUE(m1.Equals(m2)); +} + +TEST_F(EqualsTest, InterfacePtr) { + using InterfaceValueTraits = mojo::internal::ValueTraits<SomeInterfacePtr>; + + SomeInterfacePtr inf1; + SomeInterfacePtr inf2; + + EXPECT_TRUE(InterfaceValueTraits::Equals(inf1, inf1)); + EXPECT_TRUE(InterfaceValueTraits::Equals(inf1, inf2)); + + auto inf1_request = GetProxy(&inf1); + MOJO_ALLOW_UNUSED_LOCAL(inf1_request); + + EXPECT_TRUE(InterfaceValueTraits::Equals(inf1, inf1)); + EXPECT_FALSE(InterfaceValueTraits::Equals(inf1, inf2)); + + auto inf2_request = GetProxy(&inf2); + MOJO_ALLOW_UNUSED_LOCAL(inf2_request); + + EXPECT_FALSE(InterfaceValueTraits::Equals(inf1, inf2)); +} + +TEST_F(EqualsTest, InterfaceRequest) { + using RequestValueTraits = + mojo::internal::ValueTraits<InterfaceRequest<SomeInterface>>; + + InterfaceRequest<SomeInterface> req1; + InterfaceRequest<SomeInterface> req2; + + EXPECT_TRUE(RequestValueTraits::Equals(req1, req1)); + EXPECT_TRUE(RequestValueTraits::Equals(req1, req2)); + + SomeInterfacePtr inf1; + req1 = GetProxy(&inf1); + + EXPECT_TRUE(RequestValueTraits::Equals(req1, req1)); + EXPECT_FALSE(RequestValueTraits::Equals(req1, req2)); + + SomeInterfacePtr inf2; + req2 = GetProxy(&inf2); + + EXPECT_FALSE(RequestValueTraits::Equals(req1, req2)); +} + +} // test +} // mojo diff --git a/mojo/public/cpp/bindings/tests/handle_passing_unittest.cc b/mojo/public/cpp/bindings/tests/handle_passing_unittest.cc new file mode 100644 index 0000000..8487d0a --- /dev/null +++ b/mojo/public/cpp/bindings/tests/handle_passing_unittest.cc @@ -0,0 +1,353 @@ +// 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. + +#include "base/message_loop/message_loop.h" +#include "mojo/message_pump/message_pump_mojo.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/strong_binding.h" +#include "mojo/public/cpp/test_support/test_utils.h" +#include "mojo/public/interfaces/bindings/tests/sample_factory.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace test { +namespace { + +const char kText1[] = "hello"; +const char kText2[] = "world"; + +class StringRecorder { + public: + explicit StringRecorder(std::string* buf) : buf_(buf) {} + void Run(const String& a) const { *buf_ = a.To<std::string>(); } + + private: + std::string* buf_; +}; + +class ImportedInterfaceImpl : public imported::ImportedInterface { + public: + explicit ImportedInterfaceImpl( + InterfaceRequest<imported::ImportedInterface> request) + : binding_(this, request.Pass()) {} + + void DoSomething() override { do_something_count_++; } + + static int do_something_count() { return do_something_count_; } + + private: + static int do_something_count_; + Binding<ImportedInterface> binding_; +}; +int ImportedInterfaceImpl::do_something_count_ = 0; + +class SampleNamedObjectImpl : public sample::NamedObject { + public: + explicit SampleNamedObjectImpl(InterfaceRequest<sample::NamedObject> request) + : binding_(this, request.Pass()) {} + void SetName(const mojo::String& name) override { name_ = name; } + + void GetName(const mojo::Callback<void(mojo::String)>& callback) override { + callback.Run(name_); + } + + private: + std::string name_; + StrongBinding<sample::NamedObject> binding_; +}; + +class SampleFactoryImpl : public sample::Factory { + public: + explicit SampleFactoryImpl(InterfaceRequest<sample::Factory> request) + : binding_(this, request.Pass()) {} + + void DoStuff(sample::RequestPtr request, + ScopedMessagePipeHandle pipe, + const DoStuffCallback& callback) override { + std::string text1; + if (pipe.is_valid()) + EXPECT_TRUE(ReadTextMessage(pipe.get(), &text1)); + + std::string text2; + if (request->pipe.is_valid()) { + EXPECT_TRUE(ReadTextMessage(request->pipe.get(), &text2)); + + // Ensure that simply accessing request->pipe does not close it. + EXPECT_TRUE(request->pipe.is_valid()); + } + + ScopedMessagePipeHandle pipe0; + if (!text2.empty()) { + CreateMessagePipe(nullptr, &pipe0, &pipe1_); + EXPECT_TRUE(WriteTextMessage(pipe1_.get(), text2)); + } + + sample::ResponsePtr response(sample::Response::New()); + response->x = 2; + response->pipe = pipe0.Pass(); + callback.Run(response.Pass(), text1); + + if (request->obj) + request->obj->DoSomething(); + } + + void DoStuff2(ScopedDataPipeConsumerHandle pipe, + const DoStuff2Callback& callback) override { + // Read the data from the pipe, writing the response (as a string) to + // DidStuff2(). + ASSERT_TRUE(pipe.is_valid()); + uint32_t data_size = 0; + ASSERT_EQ(MOJO_RESULT_OK, + ReadDataRaw( + pipe.get(), nullptr, &data_size, MOJO_READ_DATA_FLAG_QUERY)); + ASSERT_NE(0, static_cast<int>(data_size)); + char data[64]; + ASSERT_LT(static_cast<int>(data_size), 64); + ASSERT_EQ( + MOJO_RESULT_OK, + ReadDataRaw( + pipe.get(), data, &data_size, MOJO_READ_DATA_FLAG_ALL_OR_NONE)); + + callback.Run(data); + } + + void CreateNamedObject( + InterfaceRequest<sample::NamedObject> object_request) override { + EXPECT_TRUE(object_request.is_pending()); + new SampleNamedObjectImpl(object_request.Pass()); + } + + // These aren't called or implemented, but exist here to test that the + // methods are generated with the correct argument types for imported + // interfaces. + void RequestImportedInterface( + InterfaceRequest<imported::ImportedInterface> imported, + const mojo::Callback<void(InterfaceRequest<imported::ImportedInterface>)>& + callback) override {} + void TakeImportedInterface( + imported::ImportedInterfacePtr imported, + const mojo::Callback<void(imported::ImportedInterfacePtr)>& callback) + override {} + + private: + ScopedMessagePipeHandle pipe1_; + Binding<sample::Factory> binding_; +}; + +class HandlePassingTest : public testing::Test { + public: + HandlePassingTest() : loop_(common::MessagePumpMojo::Create()) {} + + void TearDown() override { PumpMessages(); } + + void PumpMessages() { loop_.RunUntilIdle(); } + + private: + base::MessageLoop loop_; +}; + +struct DoStuffCallback { + DoStuffCallback(bool* got_response, std::string* got_text_reply) + : got_response(got_response), got_text_reply(got_text_reply) {} + + void Run(sample::ResponsePtr response, const String& text_reply) const { + *got_text_reply = text_reply; + + if (response->pipe.is_valid()) { + std::string text2; + EXPECT_TRUE(ReadTextMessage(response->pipe.get(), &text2)); + + // Ensure that simply accessing response.pipe does not close it. + EXPECT_TRUE(response->pipe.is_valid()); + + EXPECT_EQ(std::string(kText2), text2); + + // Do some more tests of handle passing: + ScopedMessagePipeHandle p = response->pipe.Pass(); + EXPECT_TRUE(p.is_valid()); + EXPECT_FALSE(response->pipe.is_valid()); + } + + *got_response = true; + } + + bool* got_response; + std::string* got_text_reply; +}; + +TEST_F(HandlePassingTest, Basic) { + sample::FactoryPtr factory; + SampleFactoryImpl factory_impl(GetProxy(&factory)); + + MessagePipe pipe0; + EXPECT_TRUE(WriteTextMessage(pipe0.handle1.get(), kText1)); + + MessagePipe pipe1; + EXPECT_TRUE(WriteTextMessage(pipe1.handle1.get(), kText2)); + + imported::ImportedInterfacePtr imported; + ImportedInterfaceImpl imported_impl(GetProxy(&imported)); + + sample::RequestPtr request(sample::Request::New()); + request->x = 1; + request->pipe = pipe1.handle0.Pass(); + request->obj = imported.Pass(); + bool got_response = false; + std::string got_text_reply; + DoStuffCallback cb(&got_response, &got_text_reply); + factory->DoStuff(request.Pass(), pipe0.handle0.Pass(), cb); + + EXPECT_FALSE(*cb.got_response); + int count_before = ImportedInterfaceImpl::do_something_count(); + + PumpMessages(); + + EXPECT_TRUE(*cb.got_response); + EXPECT_EQ(kText1, *cb.got_text_reply); + EXPECT_EQ(1, ImportedInterfaceImpl::do_something_count() - count_before); +} + +TEST_F(HandlePassingTest, PassInvalid) { + sample::FactoryPtr factory; + SampleFactoryImpl factory_impl(GetProxy(&factory)); + + sample::RequestPtr request(sample::Request::New()); + request->x = 1; + bool got_response = false; + std::string got_text_reply; + DoStuffCallback cb(&got_response, &got_text_reply); + factory->DoStuff(request.Pass(), ScopedMessagePipeHandle().Pass(), cb); + + EXPECT_FALSE(*cb.got_response); + + PumpMessages(); + + EXPECT_TRUE(*cb.got_response); +} + +struct DoStuff2Callback { + DoStuff2Callback(bool* got_response, std::string* got_text_reply) + : got_response(got_response), got_text_reply(got_text_reply) {} + + void Run(const String& text_reply) const { + *got_response = true; + *got_text_reply = text_reply; + } + + bool* got_response; + std::string* got_text_reply; +}; + +// Verifies DataPipeConsumer can be passed and read from. +TEST_F(HandlePassingTest, DataPipe) { + sample::FactoryPtr factory; + SampleFactoryImpl factory_impl(GetProxy(&factory)); + + // Writes a string to a data pipe and passes the data pipe (consumer) to the + // factory. + ScopedDataPipeProducerHandle producer_handle; + ScopedDataPipeConsumerHandle consumer_handle; + MojoCreateDataPipeOptions options = {sizeof(MojoCreateDataPipeOptions), + MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, + 1, + 1024}; + ASSERT_EQ(MOJO_RESULT_OK, + CreateDataPipe(&options, &producer_handle, &consumer_handle)); + std::string expected_text_reply = "got it"; + // +1 for \0. + uint32_t data_size = static_cast<uint32_t>(expected_text_reply.size() + 1); + ASSERT_EQ(MOJO_RESULT_OK, + WriteDataRaw(producer_handle.get(), + expected_text_reply.c_str(), + &data_size, + MOJO_WRITE_DATA_FLAG_ALL_OR_NONE)); + + bool got_response = false; + std::string got_text_reply; + DoStuff2Callback cb(&got_response, &got_text_reply); + factory->DoStuff2(consumer_handle.Pass(), cb); + + EXPECT_FALSE(*cb.got_response); + + PumpMessages(); + + EXPECT_TRUE(*cb.got_response); + EXPECT_EQ(expected_text_reply, *cb.got_text_reply); +} + +TEST_F(HandlePassingTest, PipesAreClosed) { + sample::FactoryPtr factory; + SampleFactoryImpl factory_impl(GetProxy(&factory)); + + MessagePipe extra_pipe; + + MojoHandle handle0_value = extra_pipe.handle0.get().value(); + MojoHandle handle1_value = extra_pipe.handle1.get().value(); + + { + Array<ScopedMessagePipeHandle> pipes(2); + pipes[0] = extra_pipe.handle0.Pass(); + pipes[1] = extra_pipe.handle1.Pass(); + + sample::RequestPtr request(sample::Request::New()); + request->more_pipes = pipes.Pass(); + + factory->DoStuff(request.Pass(), ScopedMessagePipeHandle(), + sample::Factory::DoStuffCallback()); + } + + // We expect the pipes to have been closed. + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(handle0_value)); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(handle1_value)); +} + +TEST_F(HandlePassingTest, IsHandle) { + // Validate that mojo::internal::IsHandle<> works as expected since this. + // template is key to ensuring that we don't leak handles. + EXPECT_TRUE(internal::IsHandle<Handle>::value); + EXPECT_TRUE(internal::IsHandle<MessagePipeHandle>::value); + EXPECT_TRUE(internal::IsHandle<DataPipeConsumerHandle>::value); + EXPECT_TRUE(internal::IsHandle<DataPipeProducerHandle>::value); + EXPECT_TRUE(internal::IsHandle<SharedBufferHandle>::value); + + // Basic sanity checks... + EXPECT_FALSE(internal::IsHandle<int>::value); + EXPECT_FALSE(internal::IsHandle<sample::FactoryPtr>::value); + EXPECT_FALSE(internal::IsHandle<String>::value); +} + +TEST_F(HandlePassingTest, CreateNamedObject) { + sample::FactoryPtr factory; + SampleFactoryImpl factory_impl(GetProxy(&factory)); + + sample::NamedObjectPtr object1; + EXPECT_FALSE(object1); + + InterfaceRequest<sample::NamedObject> object1_request = GetProxy(&object1); + EXPECT_TRUE(object1_request.is_pending()); + factory->CreateNamedObject(object1_request.Pass()); + EXPECT_FALSE(object1_request.is_pending()); // We've passed the request. + + ASSERT_TRUE(object1); + object1->SetName("object1"); + + sample::NamedObjectPtr object2; + factory->CreateNamedObject(GetProxy(&object2)); + object2->SetName("object2"); + + std::string name1; + object1->GetName(StringRecorder(&name1)); + + std::string name2; + object2->GetName(StringRecorder(&name2)); + + PumpMessages(); // Yield for results. + + EXPECT_EQ(std::string("object1"), name1); + EXPECT_EQ(std::string("object2"), name2); +} + +} // namespace +} // namespace test +} // namespace mojo diff --git a/mojo/public/cpp/bindings/tests/interface_ptr_unittest.cc b/mojo/public/cpp/bindings/tests/interface_ptr_unittest.cc new file mode 100644 index 0000000..508787a --- /dev/null +++ b/mojo/public/cpp/bindings/tests/interface_ptr_unittest.cc @@ -0,0 +1,647 @@ +// 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. + +#include "base/message_loop/message_loop.h" +#include "mojo/message_pump/message_pump_mojo.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/strong_binding.h" +#include "mojo/public/interfaces/bindings/tests/math_calculator.mojom.h" +#include "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom.h" +#include "mojo/public/interfaces/bindings/tests/sample_service.mojom.h" +#include "mojo/public/interfaces/bindings/tests/scoping.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace test { +namespace { + +template <typename Method, typename Class> +class RunnableImpl { + public: + RunnableImpl(Method method, Class instance) + : method_(method), instance_(instance) {} + template <typename... Args> + void Run(Args... args) const { + (instance_->*method_)(args...); + } + + private: + Method method_; + Class instance_; +}; + +template <typename Method, typename Class> +RunnableImpl<Method, Class> MakeRunnable(Method method, Class object) { + return RunnableImpl<Method, Class>(method, object); +} + +typedef mojo::Callback<void(double)> CalcCallback; + +class MathCalculatorImpl : public math::Calculator { + public: + explicit MathCalculatorImpl(InterfaceRequest<math::Calculator> request) + : total_(0.0), binding_(this, request.Pass()) {} + ~MathCalculatorImpl() override {} + + void CloseMessagePipe() { binding_.Close(); } + + void WaitForIncomingMethodCall() { binding_.WaitForIncomingMethodCall(); } + + void Clear(const CalcCallback& callback) override { + total_ = 0.0; + callback.Run(total_); + } + + void Add(double value, const CalcCallback& callback) override { + total_ += value; + callback.Run(total_); + } + + void Multiply(double value, const CalcCallback& callback) override { + total_ *= value; + callback.Run(total_); + } + + private: + double total_; + Binding<math::Calculator> binding_; +}; + +class MathCalculatorUI { + public: + explicit MathCalculatorUI(math::CalculatorPtr calculator) + : calculator_(calculator.Pass()), + output_(0.0), + callback_(MakeRunnable(&MathCalculatorUI::Output, this)) {} + + bool WaitForIncomingResponse() { + return calculator_.WaitForIncomingResponse(); + } + + bool encountered_error() const { return calculator_.encountered_error(); } + + void Add(double value) { calculator_->Add(value, callback_); } + + void Subtract(double value) { calculator_->Add(-value, callback_); } + + void Multiply(double value) { calculator_->Multiply(value, callback_); } + + void Divide(double value) { calculator_->Multiply(1.0 / value, callback_); } + + double GetOutput() const { return output_; } + + private: + void Output(double output) { output_ = output; } + + math::CalculatorPtr calculator_; + double output_; + Callback<void(double)> callback_; +}; + +class SelfDestructingMathCalculatorUI { + public: + explicit SelfDestructingMathCalculatorUI(math::CalculatorPtr calculator) + : calculator_(calculator.Pass()), nesting_level_(0) { + ++num_instances_; + } + + void BeginTest(bool nested) { + nesting_level_ = nested ? 2 : 1; + calculator_->Add( + 1.0, MakeRunnable(&SelfDestructingMathCalculatorUI::Output, this)); + } + + static int num_instances() { return num_instances_; } + + void Output(double value) { + if (--nesting_level_ > 0) { + // Add some more and wait for re-entrant call to Output! + calculator_->Add( + 1.0, MakeRunnable(&SelfDestructingMathCalculatorUI::Output, this)); + base::MessageLoop::current()->RunUntilIdle(); + } else { + delete this; + } + } + + private: + ~SelfDestructingMathCalculatorUI() { --num_instances_; } + + math::CalculatorPtr calculator_; + int nesting_level_; + static int num_instances_; +}; + +// static +int SelfDestructingMathCalculatorUI::num_instances_ = 0; + +class ReentrantServiceImpl : public sample::Service { + public: + ~ReentrantServiceImpl() override {} + + explicit ReentrantServiceImpl(InterfaceRequest<sample::Service> request) + : call_depth_(0), max_call_depth_(0), binding_(this, request.Pass()) {} + + int max_call_depth() { return max_call_depth_; } + + void Frobinate(sample::FooPtr foo, + sample::Service::BazOptions baz, + sample::PortPtr port, + const sample::Service::FrobinateCallback& callback) override { + max_call_depth_ = std::max(++call_depth_, max_call_depth_); + if (call_depth_ == 1) { + EXPECT_TRUE(binding_.WaitForIncomingMethodCall()); + } + call_depth_--; + callback.Run(5); + } + + void GetPort(mojo::InterfaceRequest<sample::Port> port) override {} + + private: + int call_depth_; + int max_call_depth_; + Binding<sample::Service> binding_; +}; + +class IntegerAccessorImpl : public sample::IntegerAccessor { + public: + IntegerAccessorImpl() : integer_(0) {} + ~IntegerAccessorImpl() override {} + + int64_t integer() const { return integer_; } + + private: + // sample::IntegerAccessor implementation. + void GetInteger(const GetIntegerCallback& callback) override { + callback.Run(integer_, sample::ENUM_VALUE); + } + void SetInteger(int64_t data, sample::Enum type) override { integer_ = data; } + + int64_t integer_; +}; + +class InterfacePtrTest : public testing::Test { + public: + InterfacePtrTest() : loop_(common::MessagePumpMojo::Create()) {} + ~InterfacePtrTest() override { loop_.RunUntilIdle(); } + + void PumpMessages() { loop_.RunUntilIdle(); } + + private: + base::MessageLoop loop_; +}; + +TEST_F(InterfacePtrTest, IsBound) { + math::CalculatorPtr calc; + EXPECT_FALSE(calc.is_bound()); + MathCalculatorImpl calc_impl(GetProxy(&calc)); + EXPECT_TRUE(calc.is_bound()); +} + +TEST_F(InterfacePtrTest, EndToEnd) { + math::CalculatorPtr calc; + MathCalculatorImpl calc_impl(GetProxy(&calc)); + + // Suppose this is instantiated in a process that has pipe1_. + MathCalculatorUI calculator_ui(calc.Pass()); + + calculator_ui.Add(2.0); + calculator_ui.Multiply(5.0); + + PumpMessages(); + + EXPECT_EQ(10.0, calculator_ui.GetOutput()); +} + +TEST_F(InterfacePtrTest, EndToEnd_Synchronous) { + math::CalculatorPtr calc; + MathCalculatorImpl calc_impl(GetProxy(&calc)); + + // Suppose this is instantiated in a process that has pipe1_. + MathCalculatorUI calculator_ui(calc.Pass()); + + EXPECT_EQ(0.0, calculator_ui.GetOutput()); + + calculator_ui.Add(2.0); + EXPECT_EQ(0.0, calculator_ui.GetOutput()); + calc_impl.WaitForIncomingMethodCall(); + calculator_ui.WaitForIncomingResponse(); + EXPECT_EQ(2.0, calculator_ui.GetOutput()); + + calculator_ui.Multiply(5.0); + EXPECT_EQ(2.0, calculator_ui.GetOutput()); + calc_impl.WaitForIncomingMethodCall(); + calculator_ui.WaitForIncomingResponse(); + EXPECT_EQ(10.0, calculator_ui.GetOutput()); +} + +TEST_F(InterfacePtrTest, Movable) { + math::CalculatorPtr a; + math::CalculatorPtr b; + MathCalculatorImpl calc_impl(GetProxy(&b)); + + EXPECT_TRUE(!a); + EXPECT_FALSE(!b); + + a = b.Pass(); + + EXPECT_FALSE(!a); + EXPECT_TRUE(!b); +} + +TEST_F(InterfacePtrTest, Resettable) { + math::CalculatorPtr a; + + EXPECT_TRUE(!a); + + MessagePipe pipe; + + // Save this so we can test it later. + Handle handle = pipe.handle0.get(); + + a = MakeProxy(InterfacePtrInfo<math::Calculator>(pipe.handle0.Pass(), 0u)); + + EXPECT_FALSE(!a); + + a.reset(); + + EXPECT_TRUE(!a); + EXPECT_FALSE(a.internal_state()->router_for_testing()); + + // Test that handle was closed. + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, CloseRaw(handle)); +} + +TEST_F(InterfacePtrTest, BindInvalidHandle) { + math::CalculatorPtr ptr; + EXPECT_FALSE(ptr.get()); + EXPECT_FALSE(ptr); + + ptr.Bind(InterfacePtrInfo<math::Calculator>()); + EXPECT_FALSE(ptr.get()); + EXPECT_FALSE(ptr); +} + +TEST_F(InterfacePtrTest, EncounteredError) { + math::CalculatorPtr proxy; + MathCalculatorImpl calc_impl(GetProxy(&proxy)); + + MathCalculatorUI calculator_ui(proxy.Pass()); + + calculator_ui.Add(2.0); + PumpMessages(); + EXPECT_EQ(2.0, calculator_ui.GetOutput()); + EXPECT_FALSE(calculator_ui.encountered_error()); + + calculator_ui.Multiply(5.0); + EXPECT_FALSE(calculator_ui.encountered_error()); + + // Close the server. + calc_impl.CloseMessagePipe(); + + // The state change isn't picked up locally yet. + EXPECT_FALSE(calculator_ui.encountered_error()); + + PumpMessages(); + + // OK, now we see the error. + EXPECT_TRUE(calculator_ui.encountered_error()); +} + +TEST_F(InterfacePtrTest, EncounteredErrorCallback) { + math::CalculatorPtr proxy; + MathCalculatorImpl calc_impl(GetProxy(&proxy)); + + bool encountered_error = false; + proxy.set_connection_error_handler( + [&encountered_error]() { encountered_error = true; }); + + MathCalculatorUI calculator_ui(proxy.Pass()); + + calculator_ui.Add(2.0); + PumpMessages(); + EXPECT_EQ(2.0, calculator_ui.GetOutput()); + EXPECT_FALSE(calculator_ui.encountered_error()); + + calculator_ui.Multiply(5.0); + EXPECT_FALSE(calculator_ui.encountered_error()); + + // Close the server. + calc_impl.CloseMessagePipe(); + + // The state change isn't picked up locally yet. + EXPECT_FALSE(calculator_ui.encountered_error()); + + PumpMessages(); + + // OK, now we see the error. + EXPECT_TRUE(calculator_ui.encountered_error()); + + // We should have also been able to observe the error through the error + // handler. + EXPECT_TRUE(encountered_error); +} + +TEST_F(InterfacePtrTest, DestroyInterfacePtrOnMethodResponse) { + math::CalculatorPtr proxy; + MathCalculatorImpl calc_impl(GetProxy(&proxy)); + + EXPECT_EQ(0, SelfDestructingMathCalculatorUI::num_instances()); + + SelfDestructingMathCalculatorUI* impl = + new SelfDestructingMathCalculatorUI(proxy.Pass()); + impl->BeginTest(false); + + PumpMessages(); + + EXPECT_EQ(0, SelfDestructingMathCalculatorUI::num_instances()); +} + +TEST_F(InterfacePtrTest, NestedDestroyInterfacePtrOnMethodResponse) { + math::CalculatorPtr proxy; + MathCalculatorImpl calc_impl(GetProxy(&proxy)); + + EXPECT_EQ(0, SelfDestructingMathCalculatorUI::num_instances()); + + SelfDestructingMathCalculatorUI* impl = + new SelfDestructingMathCalculatorUI(proxy.Pass()); + impl->BeginTest(true); + + PumpMessages(); + + EXPECT_EQ(0, SelfDestructingMathCalculatorUI::num_instances()); +} + +TEST_F(InterfacePtrTest, ReentrantWaitForIncomingMethodCall) { + sample::ServicePtr proxy; + ReentrantServiceImpl impl(GetProxy(&proxy)); + + proxy->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr, + sample::Service::FrobinateCallback()); + proxy->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr, + sample::Service::FrobinateCallback()); + + PumpMessages(); + + EXPECT_EQ(2, impl.max_call_depth()); +} + +TEST_F(InterfacePtrTest, QueryVersion) { + IntegerAccessorImpl impl; + sample::IntegerAccessorPtr ptr; + Binding<sample::IntegerAccessor> binding(&impl, GetProxy(&ptr)); + + EXPECT_EQ(0u, ptr.version()); + + auto callback = [](uint32_t version) { EXPECT_EQ(3u, version); }; + ptr.QueryVersion(callback); + + PumpMessages(); + + EXPECT_EQ(3u, ptr.version()); +} + +TEST_F(InterfacePtrTest, RequireVersion) { + IntegerAccessorImpl impl; + sample::IntegerAccessorPtr ptr; + Binding<sample::IntegerAccessor> binding(&impl, GetProxy(&ptr)); + + EXPECT_EQ(0u, ptr.version()); + + ptr.RequireVersion(1u); + EXPECT_EQ(1u, ptr.version()); + ptr->SetInteger(123, sample::ENUM_VALUE); + PumpMessages(); + EXPECT_FALSE(ptr.encountered_error()); + EXPECT_EQ(123, impl.integer()); + + ptr.RequireVersion(3u); + EXPECT_EQ(3u, ptr.version()); + ptr->SetInteger(456, sample::ENUM_VALUE); + PumpMessages(); + EXPECT_FALSE(ptr.encountered_error()); + EXPECT_EQ(456, impl.integer()); + + // Require a version that is not supported by the impl side. + ptr.RequireVersion(4u); + // This value is set to the input of RequireVersion() synchronously. + EXPECT_EQ(4u, ptr.version()); + ptr->SetInteger(789, sample::ENUM_VALUE); + PumpMessages(); + EXPECT_TRUE(ptr.encountered_error()); + // The call to SetInteger() after RequireVersion(4u) is ignored. + EXPECT_EQ(456, impl.integer()); +} + +class StrongMathCalculatorImpl : public math::Calculator { + public: + StrongMathCalculatorImpl(ScopedMessagePipeHandle handle, + bool* error_received, + bool* destroyed) + : error_received_(error_received), + destroyed_(destroyed), + binding_(this, handle.Pass()) { + binding_.set_connection_error_handler( + [this]() { *error_received_ = true; }); + } + ~StrongMathCalculatorImpl() override { *destroyed_ = true; } + + // math::Calculator implementation. + void Clear(const CalcCallback& callback) override { callback.Run(total_); } + + void Add(double value, const CalcCallback& callback) override { + total_ += value; + callback.Run(total_); + } + + void Multiply(double value, const CalcCallback& callback) override { + total_ *= value; + callback.Run(total_); + } + + private: + double total_ = 0.0; + bool* error_received_; + bool* destroyed_; + + StrongBinding<math::Calculator> binding_; +}; + +TEST(StrongConnectorTest, Math) { + base::MessageLoop loop(common::MessagePumpMojo::Create()); + + bool error_received = false; + bool destroyed = false; + MessagePipe pipe; + new StrongMathCalculatorImpl(pipe.handle0.Pass(), &error_received, + &destroyed); + + math::CalculatorPtr calc; + calc.Bind(InterfacePtrInfo<math::Calculator>(pipe.handle1.Pass(), 0u)); + + { + // Suppose this is instantiated in a process that has the other end of the + // message pipe. + MathCalculatorUI calculator_ui(calc.Pass()); + + calculator_ui.Add(2.0); + calculator_ui.Multiply(5.0); + + loop.RunUntilIdle(); + + EXPECT_EQ(10.0, calculator_ui.GetOutput()); + EXPECT_FALSE(error_received); + EXPECT_FALSE(destroyed); + } + // Destroying calculator_ui should close the pipe and generate an error on the + // other + // end which will destroy the instance since it is strongly bound. + + loop.RunUntilIdle(); + EXPECT_TRUE(error_received); + EXPECT_TRUE(destroyed); +} + +class WeakMathCalculatorImpl : public math::Calculator { + public: + WeakMathCalculatorImpl(ScopedMessagePipeHandle handle, + bool* error_received, + bool* destroyed) + : error_received_(error_received), + destroyed_(destroyed), + binding_(this, handle.Pass()) { + binding_.set_connection_error_handler( + [this]() { *error_received_ = true; }); + } + ~WeakMathCalculatorImpl() override { *destroyed_ = true; } + + void Clear(const CalcCallback& callback) override { callback.Run(total_); } + + void Add(double value, const CalcCallback& callback) override { + total_ += value; + callback.Run(total_); + } + + void Multiply(double value, const CalcCallback& callback) override { + total_ *= value; + callback.Run(total_); + } + + private: + double total_ = 0.0; + bool* error_received_; + bool* destroyed_; + + Binding<math::Calculator> binding_; +}; + +TEST(WeakConnectorTest, Math) { + base::MessageLoop loop(common::MessagePumpMojo::Create()); + + bool error_received = false; + bool destroyed = false; + MessagePipe pipe; + WeakMathCalculatorImpl impl(pipe.handle0.Pass(), &error_received, &destroyed); + + math::CalculatorPtr calc; + calc.Bind(InterfacePtrInfo<math::Calculator>(pipe.handle1.Pass(), 0u)); + + { + // Suppose this is instantiated in a process that has the other end of the + // message pipe. + MathCalculatorUI calculator_ui(calc.Pass()); + + calculator_ui.Add(2.0); + calculator_ui.Multiply(5.0); + + loop.RunUntilIdle(); + + EXPECT_EQ(10.0, calculator_ui.GetOutput()); + EXPECT_FALSE(error_received); + EXPECT_FALSE(destroyed); + // Destroying calculator_ui should close the pipe and generate an error on + // the other + // end which will destroy the instance since it is strongly bound. + } + + loop.RunUntilIdle(); + EXPECT_TRUE(error_received); + EXPECT_FALSE(destroyed); +} + +class CImpl : public C { + public: + CImpl(bool* d_called, InterfaceRequest<C> request) + : d_called_(d_called), + binding_(this, request.Pass()) {} + ~CImpl() override {} + + private: + void D() override { + *d_called_ = true; + } + + bool* d_called_; + StrongBinding<C> binding_; +}; + +class BImpl : public B { + public: + BImpl(bool* d_called, InterfaceRequest<B> request) + : d_called_(d_called), + binding_(this, request.Pass()) {} + ~BImpl() override {} + + private: + void GetC(InterfaceRequest<C> c) override { + new CImpl(d_called_, c.Pass()); + } + + bool* d_called_; + StrongBinding<B> binding_; +}; + +class AImpl : public A { + public: + explicit AImpl(InterfaceRequest<A> request) + : d_called_(false), + binding_(this, request.Pass()) {} + ~AImpl() override {} + + bool d_called() const { return d_called_; } + + private: + void GetB(InterfaceRequest<B> b) override { + new BImpl(&d_called_, b.Pass()); + } + + bool d_called_; + Binding<A> binding_; +}; + +TEST_F(InterfacePtrTest, Scoping) { + APtr a; + AImpl a_impl(GetProxy(&a)); + + EXPECT_FALSE(a_impl.d_called()); + + { + BPtr b; + a->GetB(GetProxy(&b)); + CPtr c; + b->GetC(GetProxy(&c)); + c->D(); + } + + // While B & C have fallen out of scope, the pipes will remain until they are + // flushed. + EXPECT_FALSE(a_impl.d_called()); + PumpMessages(); + EXPECT_TRUE(a_impl.d_called()); +} + +} // namespace +} // namespace test +} // namespace mojo diff --git a/mojo/public/cpp/bindings/tests/map_unittest.cc b/mojo/public/cpp/bindings/tests/map_unittest.cc new file mode 100644 index 0000000..5b049a8 --- /dev/null +++ b/mojo/public/cpp/bindings/tests/map_unittest.cc @@ -0,0 +1,316 @@ +// 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. + +#include "mojo/public/cpp/bindings/array.h" +#include "mojo/public/cpp/bindings/lib/array_serialization.h" +#include "mojo/public/cpp/bindings/lib/bindings_internal.h" +#include "mojo/public/cpp/bindings/lib/fixed_buffer.h" +#include "mojo/public/cpp/bindings/lib/validate_params.h" +#include "mojo/public/cpp/bindings/map.h" +#include "mojo/public/cpp/bindings/string.h" +#include "mojo/public/cpp/bindings/tests/container_test_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace test { + +namespace { + +using mojo::internal::Array_Data; +using mojo::internal::ArrayValidateParams; +using mojo::internal::FixedBufferForTesting; +using mojo::internal::Map_Data; +using mojo::internal::String_Data; + +struct StringIntData { + const char* string_data; + int int_data; +} kStringIntData[] = { + {"one", 1}, + {"two", 2}, + {"three", 3}, + {"four", 4}, +}; + +const size_t kStringIntDataSize = 4; + +using MapTest = testing::Test; + +// Tests that basic Map operations work. +TEST_F(MapTest, InsertWorks) { + Map<String, int> map; + for (size_t i = 0; i < kStringIntDataSize; ++i) + map.insert(kStringIntData[i].string_data, kStringIntData[i].int_data); + + for (size_t i = 0; i < kStringIntDataSize; ++i) { + EXPECT_EQ(kStringIntData[i].int_data, + map.at(kStringIntData[i].string_data)); + } +} + +TEST_F(MapTest, TestIndexOperator) { + Map<String, int> map; + for (size_t i = 0; i < kStringIntDataSize; ++i) + map[kStringIntData[i].string_data] = kStringIntData[i].int_data; + + for (size_t i = 0; i < kStringIntDataSize; ++i) { + EXPECT_EQ(kStringIntData[i].int_data, + map.at(kStringIntData[i].string_data)); + } +} + +TEST_F(MapTest, TestIndexOperatorAsRValue) { + Map<String, int> map; + for (size_t i = 0; i < kStringIntDataSize; ++i) + map.insert(kStringIntData[i].string_data, kStringIntData[i].int_data); + + for (size_t i = 0; i < kStringIntDataSize; ++i) { + EXPECT_EQ(kStringIntData[i].int_data, map[kStringIntData[i].string_data]); + } +} + +TEST_F(MapTest, TestIndexOperatorMoveOnly) { + ASSERT_EQ(0u, MoveOnlyType::num_instances()); + mojo::Map<mojo::String, mojo::Array<int32_t>> map; + std::vector<MoveOnlyType*> value_ptrs; + + for (size_t i = 0; i < kStringIntDataSize; ++i) { + const char* key = kStringIntData[i].string_data; + Array<int32_t> array(1); + array[0] = kStringIntData[i].int_data; + map[key] = array.Pass(); + EXPECT_TRUE(map); + } + + // We now read back that data, to test the behavior of operator[]. + for (size_t i = 0; i < kStringIntDataSize; ++i) { + auto it = map.find(kStringIntData[i].string_data); + ASSERT_TRUE(it != map.end()); + ASSERT_EQ(1u, it.GetValue().size()); + EXPECT_EQ(kStringIntData[i].int_data, it.GetValue()[0]); + } +} + +TEST_F(MapTest, ConstructedFromArray) { + Array<String> keys(kStringIntDataSize); + Array<int> values(kStringIntDataSize); + for (size_t i = 0; i < kStringIntDataSize; ++i) { + keys[i] = kStringIntData[i].string_data; + values[i] = kStringIntData[i].int_data; + } + + Map<String, int> map(keys.Pass(), values.Pass()); + + for (size_t i = 0; i < kStringIntDataSize; ++i) { + EXPECT_EQ(kStringIntData[i].int_data, + map.at(mojo::String(kStringIntData[i].string_data))); + } +} + +TEST_F(MapTest, DecomposeMapTo) { + Array<String> keys(kStringIntDataSize); + Array<int> values(kStringIntDataSize); + for (size_t i = 0; i < kStringIntDataSize; ++i) { + keys[i] = kStringIntData[i].string_data; + values[i] = kStringIntData[i].int_data; + } + + Map<String, int> map(keys.Pass(), values.Pass()); + EXPECT_EQ(kStringIntDataSize, map.size()); + + Array<String> keys2; + Array<int> values2; + map.DecomposeMapTo(&keys2, &values2); + EXPECT_EQ(0u, map.size()); + + EXPECT_EQ(kStringIntDataSize, keys2.size()); + EXPECT_EQ(kStringIntDataSize, values2.size()); + + for (size_t i = 0; i < kStringIntDataSize; ++i) { + // We are not guaranteed that the copies have the same sorting as the + // originals. + String key = kStringIntData[i].string_data; + int value = kStringIntData[i].int_data; + + bool found = false; + for (size_t j = 0; j < keys2.size(); ++j) { + if (keys2[j] == key) { + EXPECT_EQ(value, values2[j]); + found = true; + break; + } + } + + EXPECT_TRUE(found); + } +} + +TEST_F(MapTest, Insert_Copyable) { + ASSERT_EQ(0u, CopyableType::num_instances()); + mojo::Map<mojo::String, CopyableType> map; + std::vector<CopyableType*> value_ptrs; + + for (size_t i = 0; i < kStringIntDataSize; ++i) { + const char* key = kStringIntData[i].string_data; + CopyableType value; + value_ptrs.push_back(value.ptr()); + map.insert(key, value); + ASSERT_EQ(i + 1, map.size()); + ASSERT_EQ(i + 1, value_ptrs.size()); + EXPECT_EQ(map.size() + 1, CopyableType::num_instances()); + EXPECT_TRUE(map.at(key).copied()); + EXPECT_EQ(value_ptrs[i], map.at(key).ptr()); + map.at(key).ResetCopied(); + EXPECT_TRUE(map); + } + + // std::map doesn't have a capacity() method like std::vector so this test is + // a lot more boring. + + map.reset(); + EXPECT_EQ(0u, CopyableType::num_instances()); +} + +TEST_F(MapTest, Insert_MoveOnly) { + ASSERT_EQ(0u, MoveOnlyType::num_instances()); + mojo::Map<mojo::String, MoveOnlyType> map; + std::vector<MoveOnlyType*> value_ptrs; + + for (size_t i = 0; i < kStringIntDataSize; ++i) { + const char* key = kStringIntData[i].string_data; + MoveOnlyType value; + value_ptrs.push_back(value.ptr()); + map.insert(key, value.Pass()); + ASSERT_EQ(i + 1, map.size()); + ASSERT_EQ(i + 1, value_ptrs.size()); + EXPECT_EQ(map.size() + 1, MoveOnlyType::num_instances()); + EXPECT_TRUE(map.at(key).moved()); + EXPECT_EQ(value_ptrs[i], map.at(key).ptr()); + map.at(key).ResetMoved(); + EXPECT_TRUE(map); + } + + // std::map doesn't have a capacity() method like std::vector so this test is + // a lot more boring. + + map.reset(); + EXPECT_EQ(0u, MoveOnlyType::num_instances()); +} + +TEST_F(MapTest, IndexOperator_MoveOnly) { + ASSERT_EQ(0u, MoveOnlyType::num_instances()); + mojo::Map<mojo::String, MoveOnlyType> map; + std::vector<MoveOnlyType*> value_ptrs; + + for (size_t i = 0; i < kStringIntDataSize; ++i) { + const char* key = kStringIntData[i].string_data; + MoveOnlyType value; + value_ptrs.push_back(value.ptr()); + map[key] = value.Pass(); + ASSERT_EQ(i + 1, map.size()); + ASSERT_EQ(i + 1, value_ptrs.size()); + EXPECT_EQ(map.size() + 1, MoveOnlyType::num_instances()); + EXPECT_TRUE(map.at(key).moved()); + EXPECT_EQ(value_ptrs[i], map.at(key).ptr()); + map.at(key).ResetMoved(); + EXPECT_TRUE(map); + } + + // std::map doesn't have a capacity() method like std::vector so this test is + // a lot more boring. + + map.reset(); + EXPECT_EQ(0u, MoveOnlyType::num_instances()); +} + +TEST_F(MapTest, STLToMojo) { + std::map<std::string, int> stl_data; + for (size_t i = 0; i < kStringIntDataSize; ++i) + stl_data[kStringIntData[i].string_data] = kStringIntData[i].int_data; + + Map<String, int32_t> mojo_data = Map<String, int32_t>::From(stl_data); + for (size_t i = 0; i < kStringIntDataSize; ++i) { + EXPECT_EQ(kStringIntData[i].int_data, + mojo_data.at(kStringIntData[i].string_data)); + } +} + +TEST_F(MapTest, MojoToSTL) { + Map<String, int32_t> mojo_map; + for (size_t i = 0; i < kStringIntDataSize; ++i) + mojo_map.insert(kStringIntData[i].string_data, kStringIntData[i].int_data); + + std::map<std::string, int> stl_map = + mojo_map.To<std::map<std::string, int>>(); + for (size_t i = 0; i < kStringIntDataSize; ++i) { + auto it = stl_map.find(kStringIntData[i].string_data); + ASSERT_TRUE(it != stl_map.end()); + EXPECT_EQ(kStringIntData[i].int_data, it->second); + } +} + +TEST_F(MapTest, MapArrayClone) { + Map<String, Array<String>> m; + for (size_t i = 0; i < kStringIntDataSize; ++i) { + Array<String> s; + s.push_back(kStringIntData[i].string_data); + m.insert(kStringIntData[i].string_data, s.Pass()); + } + + Map<String, Array<String>> m2 = m.Clone(); + + for (auto it = m2.begin(); it != m2.end(); ++it) { + ASSERT_EQ(1u, it.GetValue().size()); + EXPECT_EQ(it.GetKey(), it.GetValue().at(0)); + } +} + +TEST_F(MapTest, ArrayOfMap) { + { + Array<Map<int32_t, int8_t>> array(1); + array[0].insert(1, 42); + + size_t size = GetSerializedSize_(array); + FixedBufferForTesting buf(size); + Array_Data<Map_Data<int32_t, int8_t>*>* data; + ArrayValidateParams validate_params( + 0, false, new ArrayValidateParams(0, false, nullptr)); + SerializeArray_(array.Pass(), &buf, &data, &validate_params); + + Array<Map<int32_t, int8_t>> deserialized_array; + Deserialize_(data, &deserialized_array); + + ASSERT_EQ(1u, deserialized_array.size()); + ASSERT_EQ(1u, deserialized_array[0].size()); + ASSERT_EQ(42, deserialized_array[0].at(1)); + } + + { + Array<Map<String, Array<bool>>> array(1); + Array<bool> map_value(2); + map_value[0] = false; + map_value[1] = true; + array[0].insert("hello world", map_value.Pass()); + + size_t size = GetSerializedSize_(array); + FixedBufferForTesting buf(size); + Array_Data<Map_Data<String_Data*, Array_Data<bool>*>*>* data; + ArrayValidateParams validate_params( + 0, false, new ArrayValidateParams( + 0, false, new ArrayValidateParams(0, false, nullptr))); + SerializeArray_(array.Pass(), &buf, &data, &validate_params); + + Array<Map<String, Array<bool>>> deserialized_array; + Deserialize_(data, &deserialized_array); + + ASSERT_EQ(1u, deserialized_array.size()); + ASSERT_EQ(1u, deserialized_array[0].size()); + ASSERT_FALSE(deserialized_array[0].at("hello world")[0]); + ASSERT_TRUE(deserialized_array[0].at("hello world")[1]); + } +} + +} // namespace +} // namespace test +} // namespace mojo diff --git a/mojo/public/cpp/bindings/tests/message_queue.cc b/mojo/public/cpp/bindings/tests/message_queue.cc new file mode 100644 index 0000000..71cb4905 --- /dev/null +++ b/mojo/public/cpp/bindings/tests/message_queue.cc @@ -0,0 +1,43 @@ +// 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. + +#include "mojo/public/cpp/bindings/tests/message_queue.h" + +#include "mojo/public/cpp/bindings/message.h" +#include "mojo/public/cpp/environment/logging.h" + +namespace mojo { +namespace test { + +MessageQueue::MessageQueue() { +} + +MessageQueue::~MessageQueue() { + while (!queue_.empty()) + Pop(); +} + +bool MessageQueue::IsEmpty() const { + return queue_.empty(); +} + +void MessageQueue::Push(Message* message) { + queue_.push(new Message()); + message->MoveTo(queue_.back()); +} + +void MessageQueue::Pop(Message* message) { + MOJO_DCHECK(!queue_.empty()); + queue_.front()->MoveTo(message); + Pop(); +} + +void MessageQueue::Pop() { + MOJO_DCHECK(!queue_.empty()); + delete queue_.front(); + queue_.pop(); +} + +} // namespace test +} // namespace mojo diff --git a/mojo/public/cpp/bindings/tests/message_queue.h b/mojo/public/cpp/bindings/tests/message_queue.h new file mode 100644 index 0000000..c3091db --- /dev/null +++ b/mojo/public/cpp/bindings/tests/message_queue.h @@ -0,0 +1,43 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_TESTS_MESSAGE_QUEUE_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_TESTS_MESSAGE_QUEUE_H_ + +#include <queue> + +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { +class Message; + +namespace test { + +// A queue for Message objects. +class MessageQueue { + public: + MessageQueue(); + ~MessageQueue(); + + bool IsEmpty() const; + + // This method copies the message data and steals ownership of its handles. + void Push(Message* message); + + // Removes the next message from the queue, copying its data and transferring + // ownership of its handles to the given |message|. + void Pop(Message* message); + + private: + void Pop(); + + std::queue<Message*> queue_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(MessageQueue); +}; + +} // namespace test +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_TESTS_MESSAGE_QUEUE_H_ diff --git a/mojo/public/cpp/bindings/tests/request_response_unittest.cc b/mojo/public/cpp/bindings/tests/request_response_unittest.cc new file mode 100644 index 0000000..1e85d4a --- /dev/null +++ b/mojo/public/cpp/bindings/tests/request_response_unittest.cc @@ -0,0 +1,152 @@ +// 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. + +#include "base/message_loop/message_loop.h" +#include "mojo/message_pump/message_pump_mojo.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/test_support/test_utils.h" +#include "mojo/public/interfaces/bindings/tests/sample_import.mojom.h" +#include "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace test { +namespace { + +class ProviderImpl : public sample::Provider { + public: + explicit ProviderImpl(InterfaceRequest<sample::Provider> request) + : binding_(this, request.Pass()) {} + + void EchoString(const String& a, + const Callback<void(String)>& callback) override { + Callback<void(String)> callback_copy; + // Make sure operator= is used. + callback_copy = callback; + callback_copy.Run(a); + } + + void EchoStrings(const String& a, + const String& b, + const Callback<void(String, String)>& callback) override { + callback.Run(a, b); + } + + void EchoMessagePipeHandle( + ScopedMessagePipeHandle a, + const Callback<void(ScopedMessagePipeHandle)>& callback) override { + callback.Run(a.Pass()); + } + + void EchoEnum(sample::Enum a, + const Callback<void(sample::Enum)>& callback) override { + callback.Run(a); + } + + void EchoInt(int32_t a, const EchoIntCallback& callback) override { + callback.Run(a); + } + + Binding<sample::Provider> binding_; +}; + +class StringRecorder { + public: + explicit StringRecorder(std::string* buf) : buf_(buf) {} + void Run(const String& a) const { *buf_ = a; } + void Run(const String& a, const String& b) const { + *buf_ = a.get() + b.get(); + } + + private: + std::string* buf_; +}; + +class EnumRecorder { + public: + explicit EnumRecorder(sample::Enum* value) : value_(value) {} + void Run(sample::Enum a) const { *value_ = a; } + + private: + sample::Enum* value_; +}; + +class MessagePipeWriter { + public: + explicit MessagePipeWriter(const char* text) : text_(text) {} + void Run(ScopedMessagePipeHandle handle) const { + WriteTextMessage(handle.get(), text_); + } + + private: + std::string text_; +}; + +class RequestResponseTest : public testing::Test { + public: + RequestResponseTest() : loop_(common::MessagePumpMojo::Create()) {} + ~RequestResponseTest() override { loop_.RunUntilIdle(); } + + void PumpMessages() { loop_.RunUntilIdle(); } + + private: + base::MessageLoop loop_; +}; + +TEST_F(RequestResponseTest, EchoString) { + sample::ProviderPtr provider; + ProviderImpl provider_impl(GetProxy(&provider)); + + std::string buf; + provider->EchoString(String::From("hello"), StringRecorder(&buf)); + + PumpMessages(); + + EXPECT_EQ(std::string("hello"), buf); +} + +TEST_F(RequestResponseTest, EchoStrings) { + sample::ProviderPtr provider; + ProviderImpl provider_impl(GetProxy(&provider)); + + std::string buf; + provider->EchoStrings( + String::From("hello"), String::From(" world"), StringRecorder(&buf)); + + PumpMessages(); + + EXPECT_EQ(std::string("hello world"), buf); +} + +TEST_F(RequestResponseTest, EchoMessagePipeHandle) { + sample::ProviderPtr provider; + ProviderImpl provider_impl(GetProxy(&provider)); + + MessagePipe pipe2; + provider->EchoMessagePipeHandle(pipe2.handle1.Pass(), + MessagePipeWriter("hello")); + + PumpMessages(); + + std::string value; + ReadTextMessage(pipe2.handle0.get(), &value); + + EXPECT_EQ(std::string("hello"), value); +} + +TEST_F(RequestResponseTest, EchoEnum) { + sample::ProviderPtr provider; + ProviderImpl provider_impl(GetProxy(&provider)); + + sample::Enum value; + provider->EchoEnum(sample::ENUM_VALUE, EnumRecorder(&value)); + + PumpMessages(); + + EXPECT_EQ(sample::ENUM_VALUE, value); +} + +} // namespace +} // namespace test +} // namespace mojo diff --git a/mojo/public/cpp/bindings/tests/router_unittest.cc b/mojo/public/cpp/bindings/tests/router_unittest.cc new file mode 100644 index 0000000..c9c9f01 --- /dev/null +++ b/mojo/public/cpp/bindings/tests/router_unittest.cc @@ -0,0 +1,379 @@ +// 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. + +#include <stdlib.h> +#include <string.h> + +#include "base/message_loop/message_loop.h" +#include "mojo/message_pump/message_pump_mojo.h" +#include "mojo/public/cpp/bindings/lib/message_builder.h" +#include "mojo/public/cpp/bindings/lib/router.h" +#include "mojo/public/cpp/bindings/tests/message_queue.h" +#include "mojo/public/cpp/system/macros.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace test { +namespace { + +void AllocRequestMessage(uint32_t name, const char* text, Message* message) { + size_t payload_size = strlen(text) + 1; // Plus null terminator. + internal::RequestMessageBuilder builder(name, payload_size); + memcpy(builder.buffer()->Allocate(payload_size), text, payload_size); + + builder.message()->MoveTo(message); +} + +void AllocResponseMessage(uint32_t name, + const char* text, + uint64_t request_id, + Message* message) { + size_t payload_size = strlen(text) + 1; // Plus null terminator. + internal::ResponseMessageBuilder builder(name, payload_size, request_id); + memcpy(builder.buffer()->Allocate(payload_size), text, payload_size); + + builder.message()->MoveTo(message); +} + +class MessageAccumulator : public MessageReceiver { + public: + explicit MessageAccumulator(MessageQueue* queue) : queue_(queue) {} + + bool Accept(Message* message) override { + queue_->Push(message); + return true; + } + + private: + MessageQueue* queue_; +}; + +class ResponseGenerator : public MessageReceiverWithResponderStatus { + public: + ResponseGenerator() {} + + bool Accept(Message* message) override { return false; } + + bool AcceptWithResponder(Message* message, + MessageReceiverWithStatus* responder) override { + EXPECT_TRUE(message->has_flag(internal::kMessageExpectsResponse)); + + bool result = SendResponse( + message->name(), message->request_id(), + reinterpret_cast<const char*>(message->payload()), responder); + EXPECT_TRUE(responder->IsValid()); + delete responder; + return result; + } + + bool SendResponse(uint32_t name, + uint64_t request_id, + const char* request_string, + MessageReceiver* responder) { + Message response; + std::string response_string(request_string); + response_string += " world!"; + AllocResponseMessage(name, response_string.c_str(), request_id, &response); + + return responder->Accept(&response); + } +}; + +class LazyResponseGenerator : public ResponseGenerator { + public: + LazyResponseGenerator() : responder_(nullptr), name_(0), request_id_(0) {} + + ~LazyResponseGenerator() override { delete responder_; } + + bool AcceptWithResponder(Message* message, + MessageReceiverWithStatus* responder) override { + name_ = message->name(); + request_id_ = message->request_id(); + request_string_ = + std::string(reinterpret_cast<const char*>(message->payload())); + responder_ = responder; + return true; + } + + bool has_responder() const { return !!responder_; } + + bool responder_is_valid() const { return responder_->IsValid(); } + + // Send the response and delete the responder. + void CompleteWithResponse() { Complete(true); } + + // Delete the responder without sending a response. + void CompleteWithoutResponse() { Complete(false); } + + private: + // Completes the request handling by deleting responder_. Optionally + // also sends a response. + void Complete(bool send_response) { + if (send_response) { + SendResponse(name_, request_id_, request_string_.c_str(), responder_); + } + delete responder_; + responder_ = nullptr; + } + + MessageReceiverWithStatus* responder_; + uint32_t name_; + uint64_t request_id_; + std::string request_string_; +}; + +class RouterTest : public testing::Test { + public: + RouterTest() : loop_(common::MessagePumpMojo::Create()) {} + + void SetUp() override { + CreateMessagePipe(nullptr, &handle0_, &handle1_); + } + + void TearDown() override {} + + void PumpMessages() { loop_.RunUntilIdle(); } + + protected: + ScopedMessagePipeHandle handle0_; + ScopedMessagePipeHandle handle1_; + + private: + base::MessageLoop loop_; +}; + +TEST_F(RouterTest, BasicRequestResponse) { + internal::Router router0(handle0_.Pass(), internal::FilterChain()); + internal::Router router1(handle1_.Pass(), internal::FilterChain()); + + ResponseGenerator generator; + router1.set_incoming_receiver(&generator); + + Message request; + AllocRequestMessage(1, "hello", &request); + + MessageQueue message_queue; + router0.AcceptWithResponder(&request, new MessageAccumulator(&message_queue)); + + PumpMessages(); + + EXPECT_FALSE(message_queue.IsEmpty()); + + Message response; + message_queue.Pop(&response); + + EXPECT_EQ(std::string("hello world!"), + std::string(reinterpret_cast<const char*>(response.payload()))); + + // Send a second message on the pipe. + Message request2; + AllocRequestMessage(1, "hello again", &request2); + + router0.AcceptWithResponder(&request2, + new MessageAccumulator(&message_queue)); + + PumpMessages(); + + EXPECT_FALSE(message_queue.IsEmpty()); + + message_queue.Pop(&response); + + EXPECT_EQ(std::string("hello again world!"), + std::string(reinterpret_cast<const char*>(response.payload()))); +} + +TEST_F(RouterTest, BasicRequestResponse_Synchronous) { + internal::Router router0(handle0_.Pass(), internal::FilterChain()); + internal::Router router1(handle1_.Pass(), internal::FilterChain()); + + ResponseGenerator generator; + router1.set_incoming_receiver(&generator); + + Message request; + AllocRequestMessage(1, "hello", &request); + + MessageQueue message_queue; + router0.AcceptWithResponder(&request, new MessageAccumulator(&message_queue)); + + router1.WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE); + router0.WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE); + + EXPECT_FALSE(message_queue.IsEmpty()); + + Message response; + message_queue.Pop(&response); + + EXPECT_EQ(std::string("hello world!"), + std::string(reinterpret_cast<const char*>(response.payload()))); + + // Send a second message on the pipe. + Message request2; + AllocRequestMessage(1, "hello again", &request2); + + router0.AcceptWithResponder(&request2, + new MessageAccumulator(&message_queue)); + + router1.WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE); + router0.WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE); + + EXPECT_FALSE(message_queue.IsEmpty()); + + message_queue.Pop(&response); + + EXPECT_EQ(std::string("hello again world!"), + std::string(reinterpret_cast<const char*>(response.payload()))); +} + +TEST_F(RouterTest, RequestWithNoReceiver) { + internal::Router router0(handle0_.Pass(), internal::FilterChain()); + internal::Router router1(handle1_.Pass(), internal::FilterChain()); + + // Without an incoming receiver set on router1, we expect router0 to observe + // an error as a result of sending a message. + + Message request; + AllocRequestMessage(1, "hello", &request); + + MessageQueue message_queue; + router0.AcceptWithResponder(&request, new MessageAccumulator(&message_queue)); + + PumpMessages(); + + EXPECT_TRUE(router0.encountered_error()); + EXPECT_TRUE(router1.encountered_error()); + EXPECT_TRUE(message_queue.IsEmpty()); +} + +// Tests Router using the LazyResponseGenerator. The responses will not be +// sent until after the requests have been accepted. +TEST_F(RouterTest, LazyResponses) { + internal::Router router0(handle0_.Pass(), internal::FilterChain()); + internal::Router router1(handle1_.Pass(), internal::FilterChain()); + + LazyResponseGenerator generator; + router1.set_incoming_receiver(&generator); + + Message request; + AllocRequestMessage(1, "hello", &request); + + MessageQueue message_queue; + router0.AcceptWithResponder(&request, new MessageAccumulator(&message_queue)); + PumpMessages(); + + // The request has been received but the response has not been sent yet. + EXPECT_TRUE(message_queue.IsEmpty()); + + // Send the response. + EXPECT_TRUE(generator.responder_is_valid()); + generator.CompleteWithResponse(); + PumpMessages(); + + // Check the response. + EXPECT_FALSE(message_queue.IsEmpty()); + Message response; + message_queue.Pop(&response); + EXPECT_EQ(std::string("hello world!"), + std::string(reinterpret_cast<const char*>(response.payload()))); + + // Send a second message on the pipe. + Message request2; + AllocRequestMessage(1, "hello again", &request2); + + router0.AcceptWithResponder(&request2, + new MessageAccumulator(&message_queue)); + PumpMessages(); + + // The request has been received but the response has not been sent yet. + EXPECT_TRUE(message_queue.IsEmpty()); + + // Send the second response. + EXPECT_TRUE(generator.responder_is_valid()); + generator.CompleteWithResponse(); + PumpMessages(); + + // Check the second response. + EXPECT_FALSE(message_queue.IsEmpty()); + message_queue.Pop(&response); + EXPECT_EQ(std::string("hello again world!"), + std::string(reinterpret_cast<const char*>(response.payload()))); +} + +// Tests that if the receiving application destroys the responder_ without +// sending a response, then we trigger connection error at both sides. Moreover, +// both sides still appear to have a valid message pipe handle bound. +TEST_F(RouterTest, MissingResponses) { + internal::Router router0(handle0_.Pass(), internal::FilterChain()); + bool error_handler_called0 = false; + router0.set_connection_error_handler( + [&error_handler_called0]() { error_handler_called0 = true; }); + + internal::Router router1(handle1_.Pass(), internal::FilterChain()); + bool error_handler_called1 = false; + router1.set_connection_error_handler( + [&error_handler_called1]() { error_handler_called1 = true; }); + + LazyResponseGenerator generator; + router1.set_incoming_receiver(&generator); + + Message request; + AllocRequestMessage(1, "hello", &request); + + MessageQueue message_queue; + router0.AcceptWithResponder(&request, new MessageAccumulator(&message_queue)); + PumpMessages(); + + // The request has been received but no response has been sent. + EXPECT_TRUE(message_queue.IsEmpty()); + + // Destroy the responder MessagerReceiver but don't send any response. + generator.CompleteWithoutResponse(); + PumpMessages(); + + // Check that no response was received. + EXPECT_TRUE(message_queue.IsEmpty()); + + // Connection error handler is called at both sides. + EXPECT_TRUE(error_handler_called0); + EXPECT_TRUE(error_handler_called1); + + // The error flag is set at both sides. + EXPECT_TRUE(router0.encountered_error()); + EXPECT_TRUE(router1.encountered_error()); + + // The message pipe handle is valid at both sides. + EXPECT_TRUE(router0.is_valid()); + EXPECT_TRUE(router1.is_valid()); +} + +TEST_F(RouterTest, LateResponse) { + // Test that things won't blow up if we try to send a message to a + // MessageReceiver, which was given to us via AcceptWithResponder, + // after the router has gone away. + + LazyResponseGenerator generator; + { + internal::Router router0(handle0_.Pass(), internal::FilterChain()); + internal::Router router1(handle1_.Pass(), internal::FilterChain()); + + router1.set_incoming_receiver(&generator); + + Message request; + AllocRequestMessage(1, "hello", &request); + + MessageQueue message_queue; + router0.AcceptWithResponder(&request, + new MessageAccumulator(&message_queue)); + + PumpMessages(); + + EXPECT_TRUE(generator.has_responder()); + } + + EXPECT_FALSE(generator.responder_is_valid()); + generator.CompleteWithResponse(); // This should end up doing nothing. +} + +} // namespace +} // namespace test +} // namespace mojo diff --git a/mojo/public/cpp/bindings/tests/sample_service_unittest.cc b/mojo/public/cpp/bindings/tests/sample_service_unittest.cc new file mode 100644 index 0000000..58878a7 --- /dev/null +++ b/mojo/public/cpp/bindings/tests/sample_service_unittest.cc @@ -0,0 +1,371 @@ +// 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. + +#include <algorithm> +#include <ostream> +#include <string> + +#include "mojo/public/interfaces/bindings/tests/sample_service.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { + +template <> +struct TypeConverter<int32_t, sample::BarPtr> { + static int32_t Convert(const sample::BarPtr& bar) { + return static_cast<int32_t>(bar->alpha) << 16 | + static_cast<int32_t>(bar->beta) << 8 | + static_cast<int32_t>(bar->gamma); + } +}; + +} // namespace mojo + +namespace sample { +namespace { + +// Set this variable to true to print the message in hex. +bool g_dump_message_as_hex = false; + +// Set this variable to true to print the message in human readable form. +bool g_dump_message_as_text = false; + +// Make a sample |Foo|. +FooPtr MakeFoo() { + mojo::String name("foopy"); + + BarPtr bar(Bar::New()); + bar->alpha = 20; + bar->beta = 40; + bar->gamma = 60; + bar->type = Bar::TYPE_VERTICAL; + + mojo::Array<BarPtr> extra_bars(3); + for (size_t i = 0; i < extra_bars.size(); ++i) { + Bar::Type type = i % 2 == 0 ? Bar::TYPE_VERTICAL : Bar::TYPE_HORIZONTAL; + BarPtr bar(Bar::New()); + uint8_t base = static_cast<uint8_t>(i * 100); + bar->alpha = base; + bar->beta = base + 20; + bar->gamma = base + 40; + bar->type = type; + extra_bars[i] = bar.Pass(); + } + + mojo::Array<uint8_t> data(10); + for (size_t i = 0; i < data.size(); ++i) + data[i] = static_cast<uint8_t>(data.size() - i); + + mojo::Array<mojo::ScopedDataPipeConsumerHandle> input_streams(2); + mojo::Array<mojo::ScopedDataPipeProducerHandle> output_streams(2); + for (size_t i = 0; i < input_streams.size(); ++i) { + MojoCreateDataPipeOptions options; + options.struct_size = sizeof(MojoCreateDataPipeOptions); + options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE; + options.element_num_bytes = 1; + options.capacity_num_bytes = 1024; + mojo::ScopedDataPipeProducerHandle producer; + mojo::ScopedDataPipeConsumerHandle consumer; + mojo::CreateDataPipe(&options, &producer, &consumer); + input_streams[i] = consumer.Pass(); + output_streams[i] = producer.Pass(); + } + + mojo::Array<mojo::Array<bool>> array_of_array_of_bools(2); + for (size_t i = 0; i < 2; ++i) { + mojo::Array<bool> array_of_bools(2); + for (size_t j = 0; j < 2; ++j) + array_of_bools[j] = j; + array_of_array_of_bools[i] = array_of_bools.Pass(); + } + + mojo::MessagePipe pipe; + FooPtr foo(Foo::New()); + foo->name = name; + foo->x = 1; + foo->y = 2; + foo->a = false; + foo->b = true; + foo->c = false; + foo->bar = bar.Pass(); + foo->extra_bars = extra_bars.Pass(); + foo->data = data.Pass(); + foo->source = pipe.handle1.Pass(); + foo->input_streams = input_streams.Pass(); + foo->output_streams = output_streams.Pass(); + foo->array_of_array_of_bools = array_of_array_of_bools.Pass(); + + return foo.Pass(); +} + +// Check that the given |Foo| is identical to the one made by |MakeFoo()|. +void CheckFoo(const Foo& foo) { + const std::string kName("foopy"); + ASSERT_FALSE(foo.name.is_null()); + EXPECT_EQ(kName.size(), foo.name.size()); + for (size_t i = 0; i < std::min(kName.size(), foo.name.size()); i++) { + // Test both |operator[]| and |at|. + EXPECT_EQ(kName[i], foo.name.at(i)) << i; + EXPECT_EQ(kName[i], foo.name[i]) << i; + } + EXPECT_EQ(kName, foo.name.get()); + + EXPECT_EQ(1, foo.x); + EXPECT_EQ(2, foo.y); + EXPECT_FALSE(foo.a); + EXPECT_TRUE(foo.b); + EXPECT_FALSE(foo.c); + + EXPECT_EQ(20, foo.bar->alpha); + EXPECT_EQ(40, foo.bar->beta); + EXPECT_EQ(60, foo.bar->gamma); + EXPECT_EQ(Bar::TYPE_VERTICAL, foo.bar->type); + + EXPECT_EQ(3u, foo.extra_bars.size()); + for (size_t i = 0; i < foo.extra_bars.size(); i++) { + uint8_t base = static_cast<uint8_t>(i * 100); + Bar::Type type = i % 2 == 0 ? Bar::TYPE_VERTICAL : Bar::TYPE_HORIZONTAL; + EXPECT_EQ(base, foo.extra_bars[i]->alpha) << i; + EXPECT_EQ(base + 20, foo.extra_bars[i]->beta) << i; + EXPECT_EQ(base + 40, foo.extra_bars[i]->gamma) << i; + EXPECT_EQ(type, foo.extra_bars[i]->type) << i; + } + + EXPECT_EQ(10u, foo.data.size()); + for (size_t i = 0; i < foo.data.size(); ++i) { + EXPECT_EQ(static_cast<uint8_t>(foo.data.size() - i), foo.data[i]) << i; + } + + EXPECT_FALSE(foo.input_streams.is_null()); + EXPECT_EQ(2u, foo.input_streams.size()); + + EXPECT_FALSE(foo.output_streams.is_null()); + EXPECT_EQ(2u, foo.output_streams.size()); + + EXPECT_EQ(2u, foo.array_of_array_of_bools.size()); + for (size_t i = 0; i < foo.array_of_array_of_bools.size(); ++i) { + EXPECT_EQ(2u, foo.array_of_array_of_bools[i].size()); + for (size_t j = 0; j < foo.array_of_array_of_bools[i].size(); ++j) { + EXPECT_EQ(bool(j), foo.array_of_array_of_bools[i][j]); + } + } +} + +void PrintSpacer(int depth) { + for (int i = 0; i < depth; ++i) + std::cout << " "; +} + +void Print(int depth, const char* name, bool value) { + PrintSpacer(depth); + std::cout << name << ": " << (value ? "true" : "false") << std::endl; +} + +void Print(int depth, const char* name, int32_t value) { + PrintSpacer(depth); + std::cout << name << ": " << value << std::endl; +} + +void Print(int depth, const char* name, uint8_t value) { + PrintSpacer(depth); + std::cout << name << ": " << uint32_t(value) << std::endl; +} + +template <typename H> +void Print(int depth, + const char* name, + const mojo::ScopedHandleBase<H>& value) { + PrintSpacer(depth); + std::cout << name << ": 0x" << std::hex << value.get().value() << std::endl; +} + +void Print(int depth, const char* name, const mojo::String& str) { + PrintSpacer(depth); + std::cout << name << ": \"" << str.get() << "\"" << std::endl; +} + +void Print(int depth, const char* name, const BarPtr& bar) { + PrintSpacer(depth); + std::cout << name << ":" << std::endl; + if (!bar.is_null()) { + ++depth; + Print(depth, "alpha", bar->alpha); + Print(depth, "beta", bar->beta); + Print(depth, "gamma", bar->gamma); + Print(depth, "packed", bar.To<int32_t>()); + --depth; + } +} + +template <typename T> +void Print(int depth, const char* name, const mojo::Array<T>& array) { + PrintSpacer(depth); + std::cout << name << ":" << std::endl; + if (!array.is_null()) { + ++depth; + for (size_t i = 0; i < array.size(); ++i) { + std::stringstream buf; + buf << i; + Print(depth, buf.str().data(), array.at(i)); + } + --depth; + } +} + +void Print(int depth, const char* name, const FooPtr& foo) { + PrintSpacer(depth); + std::cout << name << ":" << std::endl; + if (!foo.is_null()) { + ++depth; + Print(depth, "name", foo->name); + Print(depth, "x", foo->x); + Print(depth, "y", foo->y); + Print(depth, "a", foo->a); + Print(depth, "b", foo->b); + Print(depth, "c", foo->c); + Print(depth, "bar", foo->bar); + Print(depth, "extra_bars", foo->extra_bars); + Print(depth, "data", foo->data); + Print(depth, "source", foo->source); + Print(depth, "input_streams", foo->input_streams); + Print(depth, "output_streams", foo->output_streams); + Print(depth, "array_of_array_of_bools", foo->array_of_array_of_bools); + --depth; + } +} + +void DumpHex(const uint8_t* bytes, uint32_t num_bytes) { + for (uint32_t i = 0; i < num_bytes; ++i) { + std::cout << std::setw(2) << std::setfill('0') << std::hex + << uint32_t(bytes[i]); + + if (i % 16 == 15) { + std::cout << std::endl; + continue; + } + + if (i % 2 == 1) + std::cout << " "; + if (i % 8 == 7) + std::cout << " "; + } +} + +class ServiceImpl : public Service { + public: + void Frobinate(FooPtr foo, + BazOptions baz, + PortPtr port, + const Service::FrobinateCallback& callback) override { + // Users code goes here to handle the incoming Frobinate message. + + // We mainly check that we're given the expected arguments. + EXPECT_FALSE(foo.is_null()); + if (!foo.is_null()) + CheckFoo(*foo); + EXPECT_EQ(BAZ_OPTIONS_EXTRA, baz); + + if (g_dump_message_as_text) { + // Also dump the Foo structure and all of its members. + std::cout << "Frobinate:" << std::endl; + int depth = 1; + Print(depth, "foo", foo); + Print(depth, "baz", baz); + Print(depth, "port", port.get()); + } + callback.Run(5); + } + + void GetPort(mojo::InterfaceRequest<Port> port_request) override {} +}; + +class ServiceProxyImpl : public ServiceProxy { + public: + explicit ServiceProxyImpl(mojo::MessageReceiverWithResponder* receiver) + : ServiceProxy(receiver) {} +}; + +class SimpleMessageReceiver : public mojo::MessageReceiverWithResponder { + public: + bool Accept(mojo::Message* message) override { + // Imagine some IPC happened here. + + if (g_dump_message_as_hex) { + DumpHex(reinterpret_cast<const uint8_t*>(message->data()), + message->data_num_bytes()); + } + + // In the receiving process, an implementation of ServiceStub is known to + // the system. It receives the incoming message. + ServiceImpl impl; + + ServiceStub stub; + stub.set_sink(&impl); + return stub.Accept(message); + } + + bool AcceptWithResponder(mojo::Message* message, + mojo::MessageReceiver* responder) override { + return false; + } +}; + +using BindingsSampleTest = testing::Test; + +TEST_F(BindingsSampleTest, Basic) { + SimpleMessageReceiver receiver; + + // User has a proxy to a Service somehow. + Service* service = new ServiceProxyImpl(&receiver); + + // User constructs a message to send. + + // Notice that it doesn't matter in what order the structs / arrays are + // allocated. Here, the various members of Foo are allocated before Foo is + // allocated. + + FooPtr foo = MakeFoo(); + CheckFoo(*foo); + + PortPtr port; + service->Frobinate(foo.Pass(), Service::BAZ_OPTIONS_EXTRA, port.Pass(), + Service::FrobinateCallback()); + + delete service; +} + +TEST_F(BindingsSampleTest, DefaultValues) { + DefaultsTestPtr defaults(DefaultsTest::New()); + EXPECT_EQ(-12, defaults->a0); + EXPECT_EQ(kTwelve, defaults->a1); + EXPECT_EQ(1234, defaults->a2); + EXPECT_EQ(34567U, defaults->a3); + EXPECT_EQ(123456, defaults->a4); + EXPECT_EQ(3456789012U, defaults->a5); + EXPECT_EQ(-111111111111LL, defaults->a6); + EXPECT_EQ(9999999999999999999ULL, defaults->a7); + EXPECT_EQ(0x12345, defaults->a8); + EXPECT_EQ(-0x12345, defaults->a9); + EXPECT_EQ(1234, defaults->a10); + EXPECT_TRUE(defaults->a11); + EXPECT_FALSE(defaults->a12); + EXPECT_FLOAT_EQ(123.25f, defaults->a13); + EXPECT_DOUBLE_EQ(1234567890.123, defaults->a14); + EXPECT_DOUBLE_EQ(1E10, defaults->a15); + EXPECT_DOUBLE_EQ(-1.2E+20, defaults->a16); + EXPECT_DOUBLE_EQ(1.23E-20, defaults->a17); + EXPECT_TRUE(defaults->a18.is_null()); + EXPECT_TRUE(defaults->a19.is_null()); + EXPECT_EQ(Bar::TYPE_BOTH, defaults->a20); + EXPECT_TRUE(defaults->a21.is_null()); + ASSERT_FALSE(defaults->a22.is_null()); + EXPECT_EQ(imported::SHAPE_RECTANGLE, defaults->a22->shape); + EXPECT_EQ(imported::COLOR_BLACK, defaults->a22->color); + EXPECT_EQ(0xFFFFFFFFFFFFFFFFULL, defaults->a23); + EXPECT_EQ(0x123456789, defaults->a24); + EXPECT_EQ(-0x123456789, defaults->a25); +} + +} // namespace +} // namespace sample diff --git a/mojo/public/cpp/bindings/tests/serialization_warning_unittest.cc b/mojo/public/cpp/bindings/tests/serialization_warning_unittest.cc new file mode 100644 index 0000000..15da051 --- /dev/null +++ b/mojo/public/cpp/bindings/tests/serialization_warning_unittest.cc @@ -0,0 +1,227 @@ +// 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. + +// Serialization warnings are only recorded in debug build. +#ifndef NDEBUG + +#include "mojo/public/cpp/bindings/array.h" +#include "mojo/public/cpp/bindings/lib/array_internal.h" +#include "mojo/public/cpp/bindings/lib/array_serialization.h" +#include "mojo/public/cpp/bindings/lib/fixed_buffer.h" +#include "mojo/public/cpp/bindings/lib/validation_errors.h" +#include "mojo/public/cpp/bindings/string.h" +#include "mojo/public/cpp/system/message_pipe.h" +#include "mojo/public/interfaces/bindings/tests/serialization_test_structs.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace test { +namespace { + +using mojo::internal::ArrayValidateParams; + +// Creates an array of arrays of handles (2 X 3) for testing. +Array<Array<ScopedHandle>> CreateTestNestedHandleArray() { + Array<Array<ScopedHandle>> array(2); + for (size_t i = 0; i < array.size(); ++i) { + Array<ScopedHandle> nested_array(3); + for (size_t j = 0; j < nested_array.size(); ++j) { + MessagePipe pipe; + nested_array[j] = ScopedHandle::From(pipe.handle1.Pass()); + } + array[i] = nested_array.Pass(); + } + + return array.Pass(); +} + +class SerializationWarningTest : public testing::Test { + public: + ~SerializationWarningTest() override {} + + protected: + template <typename T> + void TestWarning(StructPtr<T> obj, + mojo::internal::ValidationError expected_warning) { + TestStructWarningImpl<T>(obj.Pass(), expected_warning); + } + + template <typename T> + void TestWarning(InlinedStructPtr<T> obj, + mojo::internal::ValidationError expected_warning) { + TestStructWarningImpl<T>(obj.Pass(), expected_warning); + } + + template <typename T, typename TPtr> + void TestStructWarningImpl(TPtr obj, + mojo::internal::ValidationError expected_warning) { + warning_observer_.set_last_warning(mojo::internal::VALIDATION_ERROR_NONE); + + mojo::internal::FixedBufferForTesting buf(GetSerializedSize_(obj)); + typename T::Data_* data; + Serialize_(obj.Pass(), &buf, &data); + + EXPECT_EQ(expected_warning, warning_observer_.last_warning()); + } + + template <typename T> + void TestArrayWarning(T obj, + mojo::internal::ValidationError expected_warning, + const ArrayValidateParams* validate_params) { + warning_observer_.set_last_warning(mojo::internal::VALIDATION_ERROR_NONE); + + mojo::internal::FixedBufferForTesting buf(GetSerializedSize_(obj)); + typename T::Data_* data; + SerializeArray_(obj.Pass(), &buf, &data, validate_params); + + EXPECT_EQ(expected_warning, warning_observer_.last_warning()); + } + + mojo::internal::SerializationWarningObserverForTesting warning_observer_; +}; + +TEST_F(SerializationWarningTest, HandleInStruct) { + Struct2Ptr test_struct(Struct2::New()); + EXPECT_FALSE(test_struct->hdl.is_valid()); + + TestWarning(test_struct.Pass(), + mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE); + + test_struct = Struct2::New(); + MessagePipe pipe; + test_struct->hdl = ScopedHandle::From(pipe.handle1.Pass()); + + TestWarning(test_struct.Pass(), mojo::internal::VALIDATION_ERROR_NONE); +} + +TEST_F(SerializationWarningTest, StructInStruct) { + Struct3Ptr test_struct(Struct3::New()); + EXPECT_TRUE(!test_struct->struct_1); + + TestWarning(test_struct.Pass(), + mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER); + + test_struct = Struct3::New(); + test_struct->struct_1 = Struct1::New(); + + TestWarning(test_struct.Pass(), mojo::internal::VALIDATION_ERROR_NONE); +} + +TEST_F(SerializationWarningTest, ArrayOfStructsInStruct) { + Struct4Ptr test_struct(Struct4::New()); + EXPECT_TRUE(!test_struct->data); + + TestWarning(test_struct.Pass(), + mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER); + + test_struct = Struct4::New(); + test_struct->data.resize(1); + + TestWarning(test_struct.Pass(), + mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER); + + test_struct = Struct4::New(); + test_struct->data.resize(0); + + TestWarning(test_struct.Pass(), mojo::internal::VALIDATION_ERROR_NONE); + + test_struct = Struct4::New(); + test_struct->data.resize(1); + test_struct->data[0] = Struct1::New(); + + TestWarning(test_struct.Pass(), mojo::internal::VALIDATION_ERROR_NONE); +} + +TEST_F(SerializationWarningTest, FixedArrayOfStructsInStruct) { + Struct5Ptr test_struct(Struct5::New()); + EXPECT_TRUE(!test_struct->pair); + + TestWarning(test_struct.Pass(), + mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER); + + test_struct = Struct5::New(); + test_struct->pair.resize(1); + test_struct->pair[0] = Struct1::New(); + + TestWarning(test_struct.Pass(), + mojo::internal::VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER); + + test_struct = Struct5::New(); + test_struct->pair.resize(2); + test_struct->pair[0] = Struct1::New(); + test_struct->pair[1] = Struct1::New(); + + TestWarning(test_struct.Pass(), mojo::internal::VALIDATION_ERROR_NONE); +} + +TEST_F(SerializationWarningTest, StringInStruct) { + Struct6Ptr test_struct(Struct6::New()); + EXPECT_TRUE(!test_struct->str); + + TestWarning(test_struct.Pass(), + mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER); + + test_struct = Struct6::New(); + test_struct->str = "hello world"; + + TestWarning(test_struct.Pass(), mojo::internal::VALIDATION_ERROR_NONE); +} + +TEST_F(SerializationWarningTest, ArrayOfArraysOfHandles) { + Array<Array<ScopedHandle>> test_array = CreateTestNestedHandleArray(); + test_array[0] = Array<ScopedHandle>(); + test_array[1][0] = ScopedHandle(); + + ArrayValidateParams validate_params_0( + 0, true, new ArrayValidateParams(0, true, nullptr)); + TestArrayWarning(test_array.Pass(), mojo::internal::VALIDATION_ERROR_NONE, + &validate_params_0); + + test_array = CreateTestNestedHandleArray(); + test_array[0] = Array<ScopedHandle>(); + ArrayValidateParams validate_params_1( + 0, false, new ArrayValidateParams(0, true, nullptr)); + TestArrayWarning(test_array.Pass(), + mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, + &validate_params_1); + + test_array = CreateTestNestedHandleArray(); + test_array[1][0] = ScopedHandle(); + ArrayValidateParams validate_params_2( + 0, true, new ArrayValidateParams(0, false, nullptr)); + TestArrayWarning(test_array.Pass(), + mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE, + &validate_params_2); +} + +TEST_F(SerializationWarningTest, ArrayOfStrings) { + Array<String> test_array(3); + for (size_t i = 0; i < test_array.size(); ++i) + test_array[i] = "hello"; + + ArrayValidateParams validate_params_0( + 0, true, new ArrayValidateParams(0, false, nullptr)); + TestArrayWarning(test_array.Pass(), mojo::internal::VALIDATION_ERROR_NONE, + &validate_params_0); + + test_array = Array<String>(3); + ArrayValidateParams validate_params_1( + 0, false, new ArrayValidateParams(0, false, nullptr)); + TestArrayWarning(test_array.Pass(), + mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, + &validate_params_1); + + test_array = Array<String>(2); + ArrayValidateParams validate_params_2( + 3, true, new ArrayValidateParams(0, false, nullptr)); + TestArrayWarning(test_array.Pass(), + mojo::internal::VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER, + &validate_params_2); +} + +} // namespace +} // namespace test +} // namespace mojo + +#endif diff --git a/mojo/public/cpp/bindings/tests/string_unittest.cc b/mojo/public/cpp/bindings/tests/string_unittest.cc new file mode 100644 index 0000000..f6bc424 --- /dev/null +++ b/mojo/public/cpp/bindings/tests/string_unittest.cc @@ -0,0 +1,93 @@ +// 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. + +#include "mojo/public/cpp/bindings/string.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace test { + +TEST(StringTest, DefaultIsNull) { + String s; + EXPECT_TRUE(s.is_null()); +} + +TEST(StringTest, ConstructedWithNULL) { + String s(nullptr); + EXPECT_TRUE(s.is_null()); +} + +TEST(StringTest, ConstructedWithNullCharPointer) { + const char* null = nullptr; + String s(null); + EXPECT_TRUE(s.is_null()); +} + +TEST(StringTest, AssignedNULL) { + String s(""); + EXPECT_FALSE(s.is_null()); + s = nullptr; + EXPECT_TRUE(s.is_null()); +} + +TEST(StringTest, Empty) { + String s(""); + EXPECT_FALSE(s.is_null()); + EXPECT_TRUE(s.get().empty()); +} + +TEST(StringTest, Basic) { + String s("hello world"); + EXPECT_EQ(std::string("hello world"), s.get()); +} + +TEST(StringTest, Assignment) { + String s("hello world"); + String t = s; // Makes a copy. + EXPECT_FALSE(t.is_null()); + EXPECT_EQ(std::string("hello world"), t.get()); + EXPECT_FALSE(s.is_null()); +} + +TEST(StringTest, Equality) { + String s("hello world"); + String t("hello world"); + EXPECT_EQ(s, t); + EXPECT_TRUE(s == s); + EXPECT_FALSE(s != s); + EXPECT_TRUE(s == t); + EXPECT_FALSE(s != t); + EXPECT_TRUE("hello world" == s); + EXPECT_TRUE(s == "hello world"); + EXPECT_TRUE("not" != s); + EXPECT_FALSE("not" == s); + EXPECT_TRUE(s != "not"); + EXPECT_FALSE(s == "not"); + + // Test null strings. + String n1; + String n2; + EXPECT_TRUE(n1 == n1); + EXPECT_FALSE(n1 != n2); + EXPECT_TRUE(n1 == n2); + EXPECT_FALSE(n1 != n2); + EXPECT_TRUE(n1 != s); + EXPECT_FALSE(n1 == s); + EXPECT_TRUE(s != n1); + EXPECT_FALSE(s == n1); +} + +TEST(StringTest, LessThanNullness) { + String null; + String null2; + EXPECT_FALSE(null < null2); + EXPECT_FALSE(null2 < null); + + String real("real"); + EXPECT_TRUE(null < real); + EXPECT_FALSE(real < null); +} + +} // namespace test +} // namespace mojo diff --git a/mojo/public/cpp/bindings/tests/struct_unittest.cc b/mojo/public/cpp/bindings/tests/struct_unittest.cc new file mode 100644 index 0000000..24173e5 --- /dev/null +++ b/mojo/public/cpp/bindings/tests/struct_unittest.cc @@ -0,0 +1,415 @@ +// 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. + +#include <string.h> + +#include "mojo/public/cpp/bindings/lib/fixed_buffer.h" +#include "mojo/public/cpp/system/message_pipe.h" +#include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace test { +namespace { + +RectPtr MakeRect(int32_t factor = 1) { + RectPtr rect(Rect::New()); + rect->x = 1 * factor; + rect->y = 2 * factor; + rect->width = 10 * factor; + rect->height = 20 * factor; + return rect.Pass(); +} + +void CheckRect(const Rect& rect, int32_t factor = 1) { + EXPECT_EQ(1 * factor, rect.x); + EXPECT_EQ(2 * factor, rect.y); + EXPECT_EQ(10 * factor, rect.width); + EXPECT_EQ(20 * factor, rect.height); +} + +MultiVersionStructPtr MakeMultiVersionStruct() { + MultiVersionStructPtr output(MultiVersionStruct::New()); + output->f_int32 = 123; + output->f_rect = MakeRect(5); + output->f_string = "hello"; + output->f_array = Array<int8_t>(3); + output->f_array[0] = 10; + output->f_array[1] = 9; + output->f_array[2] = 8; + MessagePipe pipe; + output->f_message_pipe = pipe.handle0.Pass(); + output->f_int16 = 42; + + return output.Pass(); +} + +template <typename U, typename T> +U SerializeAndDeserialize(T input) { + typedef typename mojo::internal::WrapperTraits<T>::DataType InputDataType; + typedef typename mojo::internal::WrapperTraits<U>::DataType OutputDataType; + + size_t size = GetSerializedSize_(input); + mojo::internal::FixedBufferForTesting buf(size + 32); + InputDataType data; + Serialize_(input.Pass(), &buf, &data); + + std::vector<Handle> handles; + data->EncodePointersAndHandles(&handles); + + // Set the subsequent area to a special value, so that we can find out if we + // mistakenly access the area. + void* subsequent_area = buf.Allocate(32); + memset(subsequent_area, 0xAA, 32); + + OutputDataType output_data = reinterpret_cast<OutputDataType>(data); + output_data->DecodePointersAndHandles(&handles); + + U output; + Deserialize_(output_data, &output); + return output.Pass(); +} + +using StructTest = testing::Test; + +} // namespace + +TEST_F(StructTest, Rect) { + RectPtr rect; + EXPECT_TRUE(rect.is_null()); + EXPECT_TRUE(!rect); + EXPECT_FALSE(rect); + + rect = nullptr; + EXPECT_TRUE(rect.is_null()); + EXPECT_TRUE(!rect); + EXPECT_FALSE(rect); + + rect = MakeRect(); + EXPECT_FALSE(rect.is_null()); + EXPECT_FALSE(!rect); + EXPECT_TRUE(rect); + + RectPtr null_rect = nullptr; + EXPECT_TRUE(null_rect.is_null()); + EXPECT_TRUE(!null_rect); + EXPECT_FALSE(null_rect); + + CheckRect(*rect); +} + +TEST_F(StructTest, Clone) { + NamedRegionPtr region; + + NamedRegionPtr clone_region = region.Clone(); + EXPECT_TRUE(clone_region.is_null()); + + region = NamedRegion::New(); + clone_region = region.Clone(); + EXPECT_TRUE(clone_region->name.is_null()); + EXPECT_TRUE(clone_region->rects.is_null()); + + region->name = "hello world"; + clone_region = region.Clone(); + EXPECT_EQ(region->name, clone_region->name); + + region->rects = Array<RectPtr>(2); + region->rects[1] = MakeRect(); + clone_region = region.Clone(); + EXPECT_EQ(2u, clone_region->rects.size()); + EXPECT_TRUE(clone_region->rects[0].is_null()); + CheckRect(*clone_region->rects[1]); + + // NoDefaultFieldValues contains handles, so Clone() is not available, but + // NoDefaultFieldValuesPtr should still compile. + NoDefaultFieldValuesPtr no_default_field_values(NoDefaultFieldValues::New()); + EXPECT_FALSE(no_default_field_values->f13.is_valid()); +} + +// Serialization test of a struct with no pointer or handle members. +TEST_F(StructTest, Serialization_Basic) { + RectPtr rect(MakeRect()); + + size_t size = GetSerializedSize_(rect); + EXPECT_EQ(8U + 16U, size); + + mojo::internal::FixedBufferForTesting buf(size); + internal::Rect_Data* data; + Serialize_(rect.Pass(), &buf, &data); + + RectPtr rect2; + Deserialize_(data, &rect2); + + CheckRect(*rect2); +} + +// Construction of a struct with struct pointers from null. +TEST_F(StructTest, Construction_StructPointers) { + RectPairPtr pair; + EXPECT_TRUE(pair.is_null()); + + pair = RectPair::New(); + EXPECT_FALSE(pair.is_null()); + EXPECT_TRUE(pair->first.is_null()); + EXPECT_TRUE(pair->first.is_null()); + + pair = nullptr; + EXPECT_TRUE(pair.is_null()); +} + +// Serialization test of a struct with struct pointers. +TEST_F(StructTest, Serialization_StructPointers) { + RectPairPtr pair(RectPair::New()); + pair->first = MakeRect(); + pair->second = MakeRect(); + + size_t size = GetSerializedSize_(pair); + EXPECT_EQ(8U + 16U + 2 * (8U + 16U), size); + + mojo::internal::FixedBufferForTesting buf(size); + internal::RectPair_Data* data; + Serialize_(pair.Pass(), &buf, &data); + + RectPairPtr pair2; + Deserialize_(data, &pair2); + + CheckRect(*pair2->first); + CheckRect(*pair2->second); +} + +// Serialization test of a struct with an array member. +TEST_F(StructTest, Serialization_ArrayPointers) { + NamedRegionPtr region(NamedRegion::New()); + region->name = "region"; + region->rects = Array<RectPtr>::New(4); + for (size_t i = 0; i < region->rects.size(); ++i) + region->rects[i] = MakeRect(static_cast<int32_t>(i) + 1); + + size_t size = GetSerializedSize_(region); + EXPECT_EQ(8U + // header + 8U + // name pointer + 8U + // rects pointer + 8U + // name header + 8U + // name payload (rounded up) + 8U + // rects header + 4 * 8U + // rects payload (four pointers) + 4 * (8U + // rect header + 16U), // rect payload (four ints) + size); + + mojo::internal::FixedBufferForTesting buf(size); + internal::NamedRegion_Data* data; + Serialize_(region.Pass(), &buf, &data); + + NamedRegionPtr region2; + Deserialize_(data, ®ion2); + + EXPECT_EQ(String("region"), region2->name); + + EXPECT_EQ(4U, region2->rects.size()); + for (size_t i = 0; i < region2->rects.size(); ++i) + CheckRect(*region2->rects[i], static_cast<int32_t>(i) + 1); +} + +// Serialization test of a struct with null array pointers. +TEST_F(StructTest, Serialization_NullArrayPointers) { + NamedRegionPtr region(NamedRegion::New()); + EXPECT_TRUE(region->name.is_null()); + EXPECT_TRUE(region->rects.is_null()); + + size_t size = GetSerializedSize_(region); + EXPECT_EQ(8U + // header + 8U + // name pointer + 8U, // rects pointer + size); + + mojo::internal::FixedBufferForTesting buf(size); + internal::NamedRegion_Data* data; + Serialize_(region.Pass(), &buf, &data); + + NamedRegionPtr region2; + Deserialize_(data, ®ion2); + + EXPECT_TRUE(region2->name.is_null()); + EXPECT_TRUE(region2->rects.is_null()); +} + +// Tests deserializing structs as a newer version. +TEST_F(StructTest, Versioning_OldToNew) { + { + MultiVersionStructV0Ptr input(MultiVersionStructV0::New()); + input->f_int32 = 123; + MultiVersionStructPtr expected_output(MultiVersionStruct::New()); + expected_output->f_int32 = 123; + + MultiVersionStructPtr output = + SerializeAndDeserialize<MultiVersionStructPtr>(input.Pass()); + EXPECT_TRUE(output); + EXPECT_TRUE(output->Equals(*expected_output)); + } + + { + MultiVersionStructV1Ptr input(MultiVersionStructV1::New()); + input->f_int32 = 123; + input->f_rect = MakeRect(5); + MultiVersionStructPtr expected_output(MultiVersionStruct::New()); + expected_output->f_int32 = 123; + expected_output->f_rect = MakeRect(5); + + MultiVersionStructPtr output = + SerializeAndDeserialize<MultiVersionStructPtr>(input.Pass()); + EXPECT_TRUE(output); + EXPECT_TRUE(output->Equals(*expected_output)); + } + + { + MultiVersionStructV3Ptr input(MultiVersionStructV3::New()); + input->f_int32 = 123; + input->f_rect = MakeRect(5); + input->f_string = "hello"; + MultiVersionStructPtr expected_output(MultiVersionStruct::New()); + expected_output->f_int32 = 123; + expected_output->f_rect = MakeRect(5); + expected_output->f_string = "hello"; + + MultiVersionStructPtr output = + SerializeAndDeserialize<MultiVersionStructPtr>(input.Pass()); + EXPECT_TRUE(output); + EXPECT_TRUE(output->Equals(*expected_output)); + } + + { + MultiVersionStructV5Ptr input(MultiVersionStructV5::New()); + input->f_int32 = 123; + input->f_rect = MakeRect(5); + input->f_string = "hello"; + input->f_array = Array<int8_t>(3); + input->f_array[0] = 10; + input->f_array[1] = 9; + input->f_array[2] = 8; + MultiVersionStructPtr expected_output(MultiVersionStruct::New()); + expected_output->f_int32 = 123; + expected_output->f_rect = MakeRect(5); + expected_output->f_string = "hello"; + expected_output->f_array = Array<int8_t>(3); + expected_output->f_array[0] = 10; + expected_output->f_array[1] = 9; + expected_output->f_array[2] = 8; + + MultiVersionStructPtr output = + SerializeAndDeserialize<MultiVersionStructPtr>(input.Pass()); + EXPECT_TRUE(output); + EXPECT_TRUE(output->Equals(*expected_output)); + } + + { + MultiVersionStructV7Ptr input(MultiVersionStructV7::New()); + input->f_int32 = 123; + input->f_rect = MakeRect(5); + input->f_string = "hello"; + input->f_array = Array<int8_t>(3); + input->f_array[0] = 10; + input->f_array[1] = 9; + input->f_array[2] = 8; + MessagePipe pipe; + input->f_message_pipe = pipe.handle0.Pass(); + + MultiVersionStructPtr expected_output(MultiVersionStruct::New()); + expected_output->f_int32 = 123; + expected_output->f_rect = MakeRect(5); + expected_output->f_string = "hello"; + expected_output->f_array = Array<int8_t>(3); + expected_output->f_array[0] = 10; + expected_output->f_array[1] = 9; + expected_output->f_array[2] = 8; + // Save the raw handle value separately so that we can compare later. + MojoHandle expected_handle = input->f_message_pipe.get().value(); + + MultiVersionStructPtr output = + SerializeAndDeserialize<MultiVersionStructPtr>(input.Pass()); + EXPECT_TRUE(output); + EXPECT_EQ(expected_handle, output->f_message_pipe.get().value()); + output->f_message_pipe.reset(); + EXPECT_TRUE(output->Equals(*expected_output)); + } +} + +// Tests deserializing structs as an older version. +TEST_F(StructTest, Versioning_NewToOld) { + { + MultiVersionStructPtr input = MakeMultiVersionStruct(); + MultiVersionStructV7Ptr expected_output(MultiVersionStructV7::New()); + expected_output->f_int32 = 123; + expected_output->f_rect = MakeRect(5); + expected_output->f_string = "hello"; + expected_output->f_array = Array<int8_t>(3); + expected_output->f_array[0] = 10; + expected_output->f_array[1] = 9; + expected_output->f_array[2] = 8; + // Save the raw handle value separately so that we can compare later. + MojoHandle expected_handle = input->f_message_pipe.get().value(); + + MultiVersionStructV7Ptr output = + SerializeAndDeserialize<MultiVersionStructV7Ptr>(input.Pass()); + EXPECT_TRUE(output); + EXPECT_EQ(expected_handle, output->f_message_pipe.get().value()); + output->f_message_pipe.reset(); + EXPECT_TRUE(output->Equals(*expected_output)); + } + + { + MultiVersionStructPtr input = MakeMultiVersionStruct(); + MultiVersionStructV5Ptr expected_output(MultiVersionStructV5::New()); + expected_output->f_int32 = 123; + expected_output->f_rect = MakeRect(5); + expected_output->f_string = "hello"; + expected_output->f_array = Array<int8_t>(3); + expected_output->f_array[0] = 10; + expected_output->f_array[1] = 9; + expected_output->f_array[2] = 8; + + MultiVersionStructV5Ptr output = + SerializeAndDeserialize<MultiVersionStructV5Ptr>(input.Pass()); + EXPECT_TRUE(output); + EXPECT_TRUE(output->Equals(*expected_output)); + } + + { + MultiVersionStructPtr input = MakeMultiVersionStruct(); + MultiVersionStructV3Ptr expected_output(MultiVersionStructV3::New()); + expected_output->f_int32 = 123; + expected_output->f_rect = MakeRect(5); + expected_output->f_string = "hello"; + + MultiVersionStructV3Ptr output = + SerializeAndDeserialize<MultiVersionStructV3Ptr>(input.Pass()); + EXPECT_TRUE(output); + EXPECT_TRUE(output->Equals(*expected_output)); + } + + { + MultiVersionStructPtr input = MakeMultiVersionStruct(); + MultiVersionStructV1Ptr expected_output(MultiVersionStructV1::New()); + expected_output->f_int32 = 123; + expected_output->f_rect = MakeRect(5); + + MultiVersionStructV1Ptr output = + SerializeAndDeserialize<MultiVersionStructV1Ptr>(input.Pass()); + EXPECT_TRUE(output); + EXPECT_TRUE(output->Equals(*expected_output)); + } + + { + MultiVersionStructPtr input = MakeMultiVersionStruct(); + MultiVersionStructV0Ptr expected_output(MultiVersionStructV0::New()); + expected_output->f_int32 = 123; + + MultiVersionStructV0Ptr output = + SerializeAndDeserialize<MultiVersionStructV0Ptr>(input.Pass()); + EXPECT_TRUE(output); + EXPECT_TRUE(output->Equals(*expected_output)); + } +} +} // namespace test +} // namespace mojo diff --git a/mojo/public/cpp/bindings/tests/type_conversion_unittest.cc b/mojo/public/cpp/bindings/tests/type_conversion_unittest.cc new file mode 100644 index 0000000..704110a --- /dev/null +++ b/mojo/public/cpp/bindings/tests/type_conversion_unittest.cc @@ -0,0 +1,204 @@ +// 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. + +#include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace { + +struct RedmondRect { + int32_t left; + int32_t top; + int32_t right; + int32_t bottom; +}; + +struct RedmondNamedRegion { + std::string name; + std::vector<RedmondRect> rects; +}; + +bool AreEqualRectArrays(const Array<test::RectPtr>& rects1, + const Array<test::RectPtr>& rects2) { + if (rects1.size() != rects2.size()) + return false; + + for (size_t i = 0; i < rects1.size(); ++i) { + if (rects1[i]->x != rects2[i]->x || rects1[i]->y != rects2[i]->y || + rects1[i]->width != rects2[i]->width || + rects1[i]->height != rects2[i]->height) { + return false; + } + } + + return true; +} + +} // namespace + +template <> +struct TypeConverter<test::RectPtr, RedmondRect> { + static test::RectPtr Convert(const RedmondRect& input) { + test::RectPtr rect(test::Rect::New()); + rect->x = input.left; + rect->y = input.top; + rect->width = input.right - input.left; + rect->height = input.bottom - input.top; + return rect.Pass(); + } +}; + +template <> +struct TypeConverter<RedmondRect, test::RectPtr> { + static RedmondRect Convert(const test::RectPtr& input) { + RedmondRect rect; + rect.left = input->x; + rect.top = input->y; + rect.right = input->x + input->width; + rect.bottom = input->y + input->height; + return rect; + } +}; + +template <> +struct TypeConverter<test::NamedRegionPtr, RedmondNamedRegion> { + static test::NamedRegionPtr Convert(const RedmondNamedRegion& input) { + test::NamedRegionPtr region(test::NamedRegion::New()); + region->name = input.name; + region->rects = Array<test::RectPtr>::From(input.rects); + return region.Pass(); + } +}; + +template <> +struct TypeConverter<RedmondNamedRegion, test::NamedRegionPtr> { + static RedmondNamedRegion Convert(const test::NamedRegionPtr& input) { + RedmondNamedRegion region; + region.name = input->name; + region.rects = input->rects.To<std::vector<RedmondRect>>(); + return region; + } +}; + +namespace test { +namespace { + +TEST(TypeConversionTest, String) { + const char kText[6] = "hello"; + + String a = std::string(kText); + String b(kText); + String c(static_cast<const char*>(kText)); + + EXPECT_EQ(std::string(kText), a.To<std::string>()); + EXPECT_EQ(std::string(kText), b.To<std::string>()); + EXPECT_EQ(std::string(kText), c.To<std::string>()); +} + +TEST(TypeConversionTest, String_Null) { + String a; + EXPECT_TRUE(a.is_null()); + EXPECT_EQ(std::string(), a.To<std::string>()); + + String b = String::From(static_cast<const char*>(nullptr)); + EXPECT_TRUE(b.is_null()); +} + +TEST(TypeConversionTest, String_Empty) { + String a = ""; + EXPECT_EQ(std::string(), a.To<std::string>()); + + String b = std::string(); + EXPECT_FALSE(b.is_null()); + EXPECT_EQ(std::string(), b.To<std::string>()); +} + +TEST(TypeConversionTest, StringWithEmbeddedNull) { + const std::string kText("hel\0lo", 6); + + String a(kText); + EXPECT_EQ(kText, a.To<std::string>()); + + // Expect truncation: + String b(kText.c_str()); + EXPECT_EQ(std::string("hel"), b.To<std::string>()); +} + +TEST(TypeConversionTest, CustomTypeConverter) { + RectPtr rect(Rect::New()); + rect->x = 10; + rect->y = 20; + rect->width = 50; + rect->height = 45; + + RedmondRect rr = rect.To<RedmondRect>(); + EXPECT_EQ(10, rr.left); + EXPECT_EQ(20, rr.top); + EXPECT_EQ(60, rr.right); + EXPECT_EQ(65, rr.bottom); + + RectPtr rect2(Rect::From(rr)); + EXPECT_EQ(rect->x, rect2->x); + EXPECT_EQ(rect->y, rect2->y); + EXPECT_EQ(rect->width, rect2->width); + EXPECT_EQ(rect->height, rect2->height); +} + +TEST(TypeConversionTest, CustomTypeConverter_Array_Null) { + Array<RectPtr> rects; + + std::vector<RedmondRect> redmond_rects = rects.To<std::vector<RedmondRect>>(); + + EXPECT_TRUE(redmond_rects.empty()); +} + +TEST(TypeConversionTest, CustomTypeConverter_Array) { + const RedmondRect kBase = {10, 20, 30, 40}; + + Array<RectPtr> rects(10); + for (size_t i = 0; i < rects.size(); ++i) { + RedmondRect rr = kBase; + rr.left += static_cast<int32_t>(i); + rr.top += static_cast<int32_t>(i); + rects[i] = Rect::From(rr); + } + + std::vector<RedmondRect> redmond_rects = rects.To<std::vector<RedmondRect>>(); + + Array<RectPtr> rects2 = Array<RectPtr>::From(redmond_rects); + EXPECT_TRUE(AreEqualRectArrays(rects, rects2)); +} + +TEST(TypeConversionTest, CustomTypeConverter_Nested) { + RedmondNamedRegion redmond_region; + redmond_region.name = "foopy"; + + const RedmondRect kBase = {10, 20, 30, 40}; + + for (size_t i = 0; i < 10; ++i) { + RedmondRect rect = kBase; + rect.left += static_cast<int32_t>(i); + rect.top += static_cast<int32_t>(i); + redmond_region.rects.push_back(rect); + } + + // Round-trip through generated struct and TypeConverter. + + NamedRegionPtr copy = NamedRegion::From(redmond_region); + RedmondNamedRegion redmond_region2 = copy.To<RedmondNamedRegion>(); + + EXPECT_EQ(redmond_region.name, redmond_region2.name); + EXPECT_EQ(redmond_region.rects.size(), redmond_region2.rects.size()); + for (size_t i = 0; i < redmond_region.rects.size(); ++i) { + EXPECT_EQ(redmond_region.rects[i].left, redmond_region2.rects[i].left); + EXPECT_EQ(redmond_region.rects[i].top, redmond_region2.rects[i].top); + EXPECT_EQ(redmond_region.rects[i].right, redmond_region2.rects[i].right); + EXPECT_EQ(redmond_region.rects[i].bottom, redmond_region2.rects[i].bottom); + } +} + +} // namespace +} // namespace test +} // namespace mojo diff --git a/mojo/public/cpp/bindings/tests/union_unittest.cc b/mojo/public/cpp/bindings/tests/union_unittest.cc new file mode 100644 index 0000000..e6fddb7 --- /dev/null +++ b/mojo/public/cpp/bindings/tests/union_unittest.cc @@ -0,0 +1,1131 @@ +// 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. + +#include <vector> + +#include "base/message_loop/message_loop.h" +#include "mojo/message_pump/message_pump_mojo.h" +#include "mojo/public/cpp/bindings/array.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/lib/array_internal.h" +#include "mojo/public/cpp/bindings/lib/array_serialization.h" +#include "mojo/public/cpp/bindings/lib/bounds_checker.h" +#include "mojo/public/cpp/bindings/lib/fixed_buffer.h" +#include "mojo/public/cpp/bindings/string.h" +#include "mojo/public/cpp/test_support/test_utils.h" +#include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h" +#include "mojo/public/interfaces/bindings/tests/test_unions.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace test { + +TEST(UnionTest, PlainOldDataGetterSetter) { + PodUnionPtr pod(PodUnion::New()); + + pod->set_f_int8(10); + EXPECT_EQ(10, pod->get_f_int8()); + EXPECT_TRUE(pod->is_f_int8()); + EXPECT_FALSE(pod->is_f_int8_other()); + EXPECT_EQ(pod->which(), PodUnion::Tag::F_INT8); + + pod->set_f_uint8(11); + EXPECT_EQ(11, pod->get_f_uint8()); + EXPECT_TRUE(pod->is_f_uint8()); + EXPECT_FALSE(pod->is_f_int8()); + EXPECT_EQ(pod->which(), PodUnion::Tag::F_UINT8); + + pod->set_f_int16(12); + EXPECT_EQ(12, pod->get_f_int16()); + EXPECT_TRUE(pod->is_f_int16()); + EXPECT_EQ(pod->which(), PodUnion::Tag::F_INT16); + + pod->set_f_uint16(13); + EXPECT_EQ(13, pod->get_f_uint16()); + EXPECT_TRUE(pod->is_f_uint16()); + EXPECT_EQ(pod->which(), PodUnion::Tag::F_UINT16); + + pod->set_f_int32(14); + EXPECT_EQ(14, pod->get_f_int32()); + EXPECT_TRUE(pod->is_f_int32()); + EXPECT_EQ(pod->which(), PodUnion::Tag::F_INT32); + + pod->set_f_uint32(static_cast<uint32_t>(15)); + EXPECT_EQ(static_cast<uint32_t>(15), pod->get_f_uint32()); + EXPECT_TRUE(pod->is_f_uint32()); + EXPECT_EQ(pod->which(), PodUnion::Tag::F_UINT32); + + pod->set_f_int64(16); + EXPECT_EQ(16, pod->get_f_int64()); + EXPECT_TRUE(pod->is_f_int64()); + EXPECT_EQ(pod->which(), PodUnion::Tag::F_INT64); + + pod->set_f_uint64(static_cast<uint64_t>(17)); + EXPECT_EQ(static_cast<uint64_t>(17), pod->get_f_uint64()); + EXPECT_TRUE(pod->is_f_uint64()); + EXPECT_EQ(pod->which(), PodUnion::Tag::F_UINT64); + + pod->set_f_float(1.5); + EXPECT_EQ(1.5, pod->get_f_float()); + EXPECT_TRUE(pod->is_f_float()); + EXPECT_EQ(pod->which(), PodUnion::Tag::F_FLOAT); + + pod->set_f_double(1.9); + EXPECT_EQ(1.9, pod->get_f_double()); + EXPECT_TRUE(pod->is_f_double()); + EXPECT_EQ(pod->which(), PodUnion::Tag::F_DOUBLE); + + pod->set_f_bool(true); + EXPECT_TRUE(pod->get_f_bool()); + pod->set_f_bool(false); + EXPECT_FALSE(pod->get_f_bool()); + EXPECT_TRUE(pod->is_f_bool()); + EXPECT_EQ(pod->which(), PodUnion::Tag::F_BOOL); + + pod->set_f_enum(AN_ENUM_SECOND); + EXPECT_EQ(AN_ENUM_SECOND, pod->get_f_enum()); + EXPECT_TRUE(pod->is_f_enum()); + EXPECT_EQ(pod->which(), PodUnion::Tag::F_ENUM); +} + +TEST(UnionTest, PodEquals) { + PodUnionPtr pod1(PodUnion::New()); + PodUnionPtr pod2(PodUnion::New()); + + pod1->set_f_int8(10); + pod2->set_f_int8(10); + EXPECT_TRUE(pod1.Equals(pod2)); + + pod2->set_f_int8(11); + EXPECT_FALSE(pod1.Equals(pod2)); + + pod2->set_f_int8_other(10); + EXPECT_FALSE(pod1.Equals(pod2)); +} + +TEST(UnionTest, PodClone) { + PodUnionPtr pod(PodUnion::New()); + pod->set_f_int8(10); + + PodUnionPtr pod_clone = pod.Clone(); + EXPECT_EQ(10, pod_clone->get_f_int8()); + EXPECT_TRUE(pod_clone->is_f_int8()); + EXPECT_EQ(pod_clone->which(), PodUnion::Tag::F_INT8); +} + +TEST(UnionTest, PodSerialization) { + PodUnionPtr pod1(PodUnion::New()); + pod1->set_f_int8(10); + + size_t size = GetSerializedSize_(pod1, false); + EXPECT_EQ(16U, size); + + mojo::internal::FixedBufferForTesting buf(size); + internal::PodUnion_Data* data = nullptr; + SerializeUnion_(pod1.Pass(), &buf, &data, false); + + PodUnionPtr pod2; + Deserialize_(data, &pod2); + + EXPECT_EQ(10, pod2->get_f_int8()); + EXPECT_TRUE(pod2->is_f_int8()); + EXPECT_EQ(pod2->which(), PodUnion::Tag::F_INT8); +} + +TEST(UnionTest, EnumSerialization) { + PodUnionPtr pod1(PodUnion::New()); + pod1->set_f_enum(AN_ENUM_SECOND); + + size_t size = GetSerializedSize_(pod1, false); + EXPECT_EQ(16U, size); + + mojo::internal::FixedBufferForTesting buf(size); + internal::PodUnion_Data* data = nullptr; + SerializeUnion_(pod1.Pass(), &buf, &data, false); + + PodUnionPtr pod2; + Deserialize_(data, &pod2); + + EXPECT_EQ(AN_ENUM_SECOND, pod2->get_f_enum()); + EXPECT_TRUE(pod2->is_f_enum()); + EXPECT_EQ(pod2->which(), PodUnion::Tag::F_ENUM); +} + +TEST(UnionTest, PodValidation) { + PodUnionPtr pod(PodUnion::New()); + pod->set_f_int8(10); + + size_t size = GetSerializedSize_(pod, false); + EXPECT_EQ(16U, size); + + mojo::internal::FixedBufferForTesting buf(size); + internal::PodUnion_Data* data = nullptr; + SerializeUnion_(pod.Pass(), &buf, &data, false); + std::vector<Handle> handles; + data->EncodePointersAndHandles(&handles); + EXPECT_TRUE(handles.empty()); + + void* raw_buf = buf.Leak(); + mojo::internal::BoundsChecker bounds_checker(data, + static_cast<uint32_t>(size), 0); + EXPECT_TRUE( + internal::PodUnion_Data::Validate(raw_buf, &bounds_checker, false)); + free(raw_buf); +} + +TEST(UnionTest, SerializeNotNull) { + PodUnionPtr pod(PodUnion::New()); + pod->set_f_int8(0); + size_t size = GetSerializedSize_(pod, false); + mojo::internal::FixedBufferForTesting buf(size); + internal::PodUnion_Data* data = nullptr; + SerializeUnion_(pod.Pass(), &buf, &data, false); + EXPECT_FALSE(data->is_null()); +} + +TEST(UnionTest, SerializeIsNullInlined) { + PodUnionPtr pod; + size_t size = GetSerializedSize_(pod, false); + EXPECT_EQ(16U, size); + mojo::internal::FixedBufferForTesting buf(size); + internal::PodUnion_Data* data = internal::PodUnion_Data::New(&buf); + + // Check that dirty output buffers are handled correctly by serialization. + data->size = 16U; + data->tag = PodUnion::Tag::F_UINT16; + data->data.f_f_int16 = 20; + + SerializeUnion_(pod.Pass(), &buf, &data, true); + EXPECT_TRUE(data->is_null()); + + PodUnionPtr pod2; + Deserialize_(data, &pod2); + EXPECT_TRUE(pod2.is_null()); +} + +TEST(UnionTest, SerializeIsNullNotInlined) { + PodUnionPtr pod; + size_t size = GetSerializedSize_(pod, false); + EXPECT_EQ(16U, size); + mojo::internal::FixedBufferForTesting buf(size); + internal::PodUnion_Data* data = nullptr; + SerializeUnion_(pod.Pass(), &buf, &data, false); + EXPECT_EQ(nullptr, data); +} + +TEST(UnionTest, NullValidation) { + void* buf = nullptr; + mojo::internal::BoundsChecker bounds_checker(buf, 0, 0); + EXPECT_TRUE(internal::PodUnion_Data::Validate(buf, &bounds_checker, false)); +} + +TEST(UnionTest, OutOfAlignmentValidation) { + size_t size = sizeof(internal::PodUnion_Data); + // Get an aligned object and shift the alignment. + mojo::internal::FixedBufferForTesting aligned_buf(size + 1); + void* raw_buf = aligned_buf.Leak(); + char* buf = reinterpret_cast<char*>(raw_buf) + 1; + + internal::PodUnion_Data* data = + reinterpret_cast<internal::PodUnion_Data*>(buf); + mojo::internal::BoundsChecker bounds_checker(data, + static_cast<uint32_t>(size), 0); + EXPECT_FALSE(internal::PodUnion_Data::Validate(buf, &bounds_checker, false)); + free(raw_buf); +} + +TEST(UnionTest, OOBValidation) { + size_t size = sizeof(internal::PodUnion_Data) - 1; + mojo::internal::FixedBufferForTesting buf(size); + internal::PodUnion_Data* data = internal::PodUnion_Data::New(&buf); + mojo::internal::BoundsChecker bounds_checker(data, + static_cast<uint32_t>(size), 0); + void* raw_buf = buf.Leak(); + EXPECT_FALSE( + internal::PodUnion_Data::Validate(raw_buf, &bounds_checker, false)); + free(raw_buf); +} + +TEST(UnionTest, UnknownTagValidation) { + size_t size = sizeof(internal::PodUnion_Data); + mojo::internal::FixedBufferForTesting buf(size); + internal::PodUnion_Data* data = internal::PodUnion_Data::New(&buf); + data->tag = static_cast<internal::PodUnion_Data::PodUnion_Tag>(0xFFFFFF); + mojo::internal::BoundsChecker bounds_checker(data, + static_cast<uint32_t>(size), 0); + void* raw_buf = buf.Leak(); + EXPECT_FALSE( + internal::PodUnion_Data::Validate(raw_buf, &bounds_checker, false)); + free(raw_buf); +} + +TEST(UnionTest, StringGetterSetter) { + ObjectUnionPtr pod(ObjectUnion::New()); + + String hello("hello world"); + pod->set_f_string(hello); + EXPECT_EQ(hello, pod->get_f_string()); + EXPECT_TRUE(pod->is_f_string()); + EXPECT_EQ(pod->which(), ObjectUnion::Tag::F_STRING); +} + +TEST(UnionTest, StringEquals) { + ObjectUnionPtr pod1(ObjectUnion::New()); + ObjectUnionPtr pod2(ObjectUnion::New()); + + pod1->set_f_string("hello world"); + pod2->set_f_string("hello world"); + EXPECT_TRUE(pod1.Equals(pod2)); + + pod2->set_f_string("hello universe"); + EXPECT_FALSE(pod1.Equals(pod2)); +} + +TEST(UnionTest, StringClone) { + ObjectUnionPtr pod(ObjectUnion::New()); + + String hello("hello world"); + pod->set_f_string(hello); + ObjectUnionPtr pod_clone = pod.Clone(); + EXPECT_EQ(hello, pod_clone->get_f_string()); + EXPECT_TRUE(pod_clone->is_f_string()); + EXPECT_EQ(pod_clone->which(), ObjectUnion::Tag::F_STRING); +} + +TEST(UnionTest, StringSerialization) { + ObjectUnionPtr pod1(ObjectUnion::New()); + + String hello("hello world"); + pod1->set_f_string(hello); + + size_t size = GetSerializedSize_(pod1, false); + mojo::internal::FixedBufferForTesting buf(size); + internal::ObjectUnion_Data* data = nullptr; + SerializeUnion_(pod1.Pass(), &buf, &data, false); + + std::vector<Handle> handles; + data->EncodePointersAndHandles(&handles); + data->DecodePointersAndHandles(&handles); + + ObjectUnionPtr pod2; + Deserialize_(data, &pod2); + EXPECT_EQ(hello, pod2->get_f_string()); + EXPECT_TRUE(pod2->is_f_string()); + EXPECT_EQ(pod2->which(), ObjectUnion::Tag::F_STRING); +} + +TEST(UnionTest, NullStringValidation) { + size_t size = sizeof(internal::ObjectUnion_Data); + mojo::internal::FixedBufferForTesting buf(size); + internal::ObjectUnion_Data* data = internal::ObjectUnion_Data::New(&buf); + data->tag = internal::ObjectUnion_Data::ObjectUnion_Tag::F_STRING; + data->data.unknown = 0x0; + mojo::internal::BoundsChecker bounds_checker(data, + static_cast<uint32_t>(size), 0); + void* raw_buf = buf.Leak(); + EXPECT_FALSE( + internal::ObjectUnion_Data::Validate(raw_buf, &bounds_checker, false)); + free(raw_buf); +} + +TEST(UnionTest, StringPointerOverflowValidation) { + size_t size = sizeof(internal::ObjectUnion_Data); + mojo::internal::FixedBufferForTesting buf(size); + internal::ObjectUnion_Data* data = internal::ObjectUnion_Data::New(&buf); + data->tag = internal::ObjectUnion_Data::ObjectUnion_Tag::F_STRING; + data->data.unknown = 0xFFFFFFFFFFFFFFFF; + mojo::internal::BoundsChecker bounds_checker(data, + static_cast<uint32_t>(size), 0); + void* raw_buf = buf.Leak(); + EXPECT_FALSE( + internal::ObjectUnion_Data::Validate(raw_buf, &bounds_checker, false)); + free(raw_buf); +} + +TEST(UnionTest, StringValidateOOB) { + size_t size = 32; + mojo::internal::FixedBufferForTesting buf(size); + internal::ObjectUnion_Data* data = internal::ObjectUnion_Data::New(&buf); + data->tag = internal::ObjectUnion_Data::ObjectUnion_Tag::F_STRING; + + data->data.f_f_string.offset = 8; + char* ptr = reinterpret_cast<char*>(&data->data.f_f_string); + mojo::internal::ArrayHeader* array_header = + reinterpret_cast<mojo::internal::ArrayHeader*>(ptr + *ptr); + array_header->num_bytes = 20; // This should go out of bounds. + array_header->num_elements = 20; + mojo::internal::BoundsChecker bounds_checker(data, 32, 0); + void* raw_buf = buf.Leak(); + EXPECT_FALSE( + internal::ObjectUnion_Data::Validate(raw_buf, &bounds_checker, false)); + free(raw_buf); +} + +// TODO(azani): Move back in array_unittest.cc when possible. +// Array tests +TEST(UnionTest, PodUnionInArray) { + SmallStructPtr small_struct(SmallStruct::New()); + small_struct->pod_union_array = Array<PodUnionPtr>(2); + small_struct->pod_union_array[0] = PodUnion::New(); + small_struct->pod_union_array[1] = PodUnion::New(); + + small_struct->pod_union_array[0]->set_f_int8(10); + small_struct->pod_union_array[1]->set_f_int16(12); + + EXPECT_EQ(10, small_struct->pod_union_array[0]->get_f_int8()); + EXPECT_EQ(12, small_struct->pod_union_array[1]->get_f_int16()); +} + +TEST(UnionTest, PodUnionInArraySerialization) { + Array<PodUnionPtr> array(2); + array[0] = PodUnion::New(); + array[1] = PodUnion::New(); + + array[0]->set_f_int8(10); + array[1]->set_f_int16(12); + EXPECT_EQ(2U, array.size()); + + size_t size = GetSerializedSize_(array); + EXPECT_EQ(40U, size); + + mojo::internal::FixedBufferForTesting buf(size); + mojo::internal::Array_Data<internal::PodUnion_Data>* data; + mojo::internal::ArrayValidateParams validate_params(0, false, nullptr); + SerializeArray_(array.Pass(), &buf, &data, &validate_params); + + Array<PodUnionPtr> array2; + Deserialize_(data, &array2); + + EXPECT_EQ(2U, array2.size()); + + EXPECT_EQ(10, array2[0]->get_f_int8()); + EXPECT_EQ(12, array2[1]->get_f_int16()); +} + +TEST(UnionTest, PodUnionInArraySerializationWithNull) { + Array<PodUnionPtr> array(2); + array[0] = PodUnion::New(); + + array[0]->set_f_int8(10); + EXPECT_EQ(2U, array.size()); + + size_t size = GetSerializedSize_(array); + EXPECT_EQ(40U, size); + + mojo::internal::FixedBufferForTesting buf(size); + mojo::internal::Array_Data<internal::PodUnion_Data>* data; + mojo::internal::ArrayValidateParams validate_params(0, true, nullptr); + SerializeArray_(array.Pass(), &buf, &data, &validate_params); + + Array<PodUnionPtr> array2; + Deserialize_(data, &array2); + + EXPECT_EQ(2U, array2.size()); + + EXPECT_EQ(10, array2[0]->get_f_int8()); + EXPECT_TRUE(array2[1].is_null()); +} + +// TODO(azani): Move back in struct_unittest.cc when possible. +// Struct tests +TEST(UnionTest, Clone_Union) { + SmallStructPtr small_struct(SmallStruct::New()); + small_struct->pod_union = PodUnion::New(); + small_struct->pod_union->set_f_int8(10); + + SmallStructPtr clone = small_struct.Clone(); + EXPECT_EQ(10, clone->pod_union->get_f_int8()); +} + +// Serialization test of a struct with a union of plain old data. +TEST(UnionTest, Serialization_UnionOfPods) { + SmallStructPtr small_struct(SmallStruct::New()); + small_struct->pod_union = PodUnion::New(); + small_struct->pod_union->set_f_int32(10); + + size_t size = GetSerializedSize_(small_struct); + + mojo::internal::FixedBufferForTesting buf(size); + internal::SmallStruct_Data* data = nullptr; + Serialize_(small_struct.Pass(), &buf, &data); + + SmallStructPtr deserialized; + Deserialize_(data, &deserialized); + + EXPECT_EQ(10, deserialized->pod_union->get_f_int32()); +} + +// Serialization test of a struct with a union of structs. +TEST(UnionTest, Serialization_UnionOfObjects) { + SmallObjStructPtr obj_struct(SmallObjStruct::New()); + obj_struct->obj_union = ObjectUnion::New(); + String hello("hello world"); + obj_struct->obj_union->set_f_string(hello); + + size_t size = GetSerializedSize_(obj_struct); + + mojo::internal::FixedBufferForTesting buf(size); + internal::SmallObjStruct_Data* data = nullptr; + Serialize_(obj_struct.Pass(), &buf, &data); + + std::vector<Handle> handles; + data->EncodePointersAndHandles(&handles); + data->DecodePointersAndHandles(&handles); + + SmallObjStructPtr deserialized; + Deserialize_(data, &deserialized); + + EXPECT_EQ(hello, deserialized->obj_union->get_f_string()); +} + +// Validation test of a struct with a union. +TEST(UnionTest, Validation_UnionsInStruct) { + SmallStructPtr small_struct(SmallStruct::New()); + small_struct->pod_union = PodUnion::New(); + small_struct->pod_union->set_f_int32(10); + + size_t size = GetSerializedSize_(small_struct); + + mojo::internal::FixedBufferForTesting buf(size); + internal::SmallStruct_Data* data = nullptr; + Serialize_(small_struct.Pass(), &buf, &data); + + std::vector<Handle> handles; + data->EncodePointersAndHandles(&handles); + EXPECT_TRUE(handles.empty()); + + void* raw_buf = buf.Leak(); + mojo::internal::BoundsChecker bounds_checker(data, + static_cast<uint32_t>(size), 0); + EXPECT_TRUE(internal::SmallStruct_Data::Validate(raw_buf, &bounds_checker)); + free(raw_buf); +} + +// Validation test of a struct union fails due to unknown union tag. +TEST(UnionTest, Validation_PodUnionInStruct_Failure) { + SmallStructPtr small_struct(SmallStruct::New()); + small_struct->pod_union = PodUnion::New(); + small_struct->pod_union->set_f_int32(10); + + size_t size = GetSerializedSize_(small_struct); + + mojo::internal::FixedBufferForTesting buf(size); + internal::SmallStruct_Data* data = nullptr; + Serialize_(small_struct.Pass(), &buf, &data); + data->pod_union.tag = static_cast<internal::PodUnion_Data::PodUnion_Tag>(100); + + std::vector<Handle> handles; + data->EncodePointersAndHandles(&handles); + EXPECT_TRUE(handles.empty()); + + void* raw_buf = buf.Leak(); + mojo::internal::BoundsChecker bounds_checker(data, + static_cast<uint32_t>(size), 0); + EXPECT_FALSE(internal::SmallStruct_Data::Validate(raw_buf, &bounds_checker)); + free(raw_buf); +} + +// Validation fails due to non-nullable null union in struct. +TEST(UnionTest, Validation_NullUnion_Failure) { + SmallStructNonNullableUnionPtr small_struct( + SmallStructNonNullableUnion::New()); + + size_t size = GetSerializedSize_(small_struct); + + mojo::internal::FixedBufferForTesting buf(size); + internal::SmallStructNonNullableUnion_Data* data = + internal::SmallStructNonNullableUnion_Data::New(&buf); + + void* raw_buf = buf.Leak(); + mojo::internal::BoundsChecker bounds_checker(data, + static_cast<uint32_t>(size), 0); + EXPECT_FALSE(internal::SmallStructNonNullableUnion_Data::Validate( + raw_buf, &bounds_checker)); + free(raw_buf); +} + +// Validation passes with nullable null union. +TEST(UnionTest, Validation_NullableUnion) { + SmallStructPtr small_struct(SmallStruct::New()); + + size_t size = GetSerializedSize_(small_struct); + + mojo::internal::FixedBufferForTesting buf(size); + internal::SmallStruct_Data* data = nullptr; + Serialize_(small_struct.Pass(), &buf, &data); + + std::vector<Handle> handles; + data->EncodePointersAndHandles(&handles); + EXPECT_TRUE(handles.empty()); + + void* raw_buf = buf.Leak(); + mojo::internal::BoundsChecker bounds_checker(data, + static_cast<uint32_t>(size), 0); + EXPECT_TRUE(internal::SmallStruct_Data::Validate(raw_buf, &bounds_checker)); + free(raw_buf); +} + +// TODO(azani): Move back in map_unittest.cc when possible. +// Map Tests +TEST(UnionTest, PodUnionInMap) { + SmallStructPtr small_struct(SmallStruct::New()); + small_struct->pod_union_map = Map<String, PodUnionPtr>(); + small_struct->pod_union_map.insert("one", PodUnion::New()); + small_struct->pod_union_map.insert("two", PodUnion::New()); + + small_struct->pod_union_map["one"]->set_f_int8(8); + small_struct->pod_union_map["two"]->set_f_int16(16); + + EXPECT_EQ(8, small_struct->pod_union_map["one"]->get_f_int8()); + EXPECT_EQ(16, small_struct->pod_union_map["two"]->get_f_int16()); +} + +TEST(UnionTest, PodUnionInMapSerialization) { + Map<String, PodUnionPtr> map; + map.insert("one", PodUnion::New()); + map.insert("two", PodUnion::New()); + + map["one"]->set_f_int8(8); + map["two"]->set_f_int16(16); + + size_t size = GetSerializedSize_(map); + EXPECT_EQ(120U, size); + + mojo::internal::FixedBufferForTesting buf(size); + mojo::internal::Map_Data<mojo::internal::String_Data*, + internal::PodUnion_Data>* data; + mojo::internal::ArrayValidateParams validate_params(0, false, nullptr); + SerializeMap_(map.Pass(), &buf, &data, &validate_params); + + Map<String, PodUnionPtr> map2; + Deserialize_(data, &map2); + + EXPECT_EQ(8, map2["one"]->get_f_int8()); + EXPECT_EQ(16, map2["two"]->get_f_int16()); +} + +TEST(UnionTest, PodUnionInMapSerializationWithNull) { + Map<String, PodUnionPtr> map; + map.insert("one", PodUnion::New()); + map.insert("two", nullptr); + + map["one"]->set_f_int8(8); + + size_t size = GetSerializedSize_(map); + EXPECT_EQ(120U, size); + + mojo::internal::FixedBufferForTesting buf(size); + mojo::internal::Map_Data<mojo::internal::String_Data*, + internal::PodUnion_Data>* data; + mojo::internal::ArrayValidateParams validate_params(0, true, nullptr); + SerializeMap_(map.Pass(), &buf, &data, &validate_params); + + Map<String, PodUnionPtr> map2; + Deserialize_(data, &map2); + + EXPECT_EQ(8, map2["one"]->get_f_int8()); + EXPECT_TRUE(map2["two"].is_null()); +} + +TEST(UnionTest, StructInUnionGetterSetterPasser) { + DummyStructPtr dummy(DummyStruct::New()); + dummy->f_int8 = 8; + + ObjectUnionPtr obj(ObjectUnion::New()); + obj->set_f_dummy(dummy.Pass()); + + EXPECT_EQ(8, obj->get_f_dummy()->f_int8); +} + +TEST(UnionTest, StructInUnionSerialization) { + DummyStructPtr dummy(DummyStruct::New()); + dummy->f_int8 = 8; + + ObjectUnionPtr obj(ObjectUnion::New()); + obj->set_f_dummy(dummy.Pass()); + + size_t size = GetSerializedSize_(obj, false); + EXPECT_EQ(32U, size); + + mojo::internal::FixedBufferForTesting buf(size); + internal::ObjectUnion_Data* data = nullptr; + SerializeUnion_(obj.Pass(), &buf, &data, false); + + std::vector<Handle> handles; + data->EncodePointersAndHandles(&handles); + data->DecodePointersAndHandles(&handles); + + ObjectUnionPtr obj2; + Deserialize_(data, &obj2); + EXPECT_EQ(8, obj2->get_f_dummy()->f_int8); +} + +TEST(UnionTest, StructInUnionValidation) { + DummyStructPtr dummy(DummyStruct::New()); + dummy->f_int8 = 8; + + ObjectUnionPtr obj(ObjectUnion::New()); + obj->set_f_dummy(dummy.Pass()); + + size_t size = GetSerializedSize_(obj, false); + + mojo::internal::FixedBufferForTesting buf(size); + internal::ObjectUnion_Data* data = nullptr; + SerializeUnion_(obj.Pass(), &buf, &data, false); + + std::vector<Handle> handles; + data->EncodePointersAndHandles(&handles); + EXPECT_TRUE(handles.empty()); + + void* raw_buf = buf.Leak(); + mojo::internal::BoundsChecker bounds_checker(data, + static_cast<uint32_t>(size), 0); + EXPECT_TRUE( + internal::ObjectUnion_Data::Validate(raw_buf, &bounds_checker, false)); + free(raw_buf); +} + +TEST(UnionTest, StructInUnionValidationNonNullable) { + DummyStructPtr dummy(nullptr); + + ObjectUnionPtr obj(ObjectUnion::New()); + obj->set_f_dummy(dummy.Pass()); + + size_t size = GetSerializedSize_(obj, false); + + mojo::internal::FixedBufferForTesting buf(size); + internal::ObjectUnion_Data* data = nullptr; + SerializeUnion_(obj.Pass(), &buf, &data, false); + + std::vector<Handle> handles; + data->EncodePointersAndHandles(&handles); + EXPECT_TRUE(handles.empty()); + + void* raw_buf = buf.Leak(); + mojo::internal::BoundsChecker bounds_checker(data, + static_cast<uint32_t>(size), 0); + EXPECT_FALSE( + internal::ObjectUnion_Data::Validate(raw_buf, &bounds_checker, false)); + free(raw_buf); +} + +TEST(UnionTest, StructInUnionValidationNullable) { + DummyStructPtr dummy(nullptr); + + ObjectUnionPtr obj(ObjectUnion::New()); + obj->set_f_nullable(dummy.Pass()); + + size_t size = GetSerializedSize_(obj, false); + + mojo::internal::FixedBufferForTesting buf(size); + internal::ObjectUnion_Data* data = nullptr; + SerializeUnion_(obj.Pass(), &buf, &data, false); + + std::vector<Handle> handles; + data->EncodePointersAndHandles(&handles); + EXPECT_TRUE(handles.empty()); + + void* raw_buf = buf.Leak(); + mojo::internal::BoundsChecker bounds_checker(data, + static_cast<uint32_t>(size), 0); + EXPECT_TRUE( + internal::ObjectUnion_Data::Validate(raw_buf, &bounds_checker, false)); + free(raw_buf); +} + +TEST(UnionTest, ArrayInUnionGetterSetter) { + Array<int8_t> array(2); + array[0] = 8; + array[1] = 9; + + ObjectUnionPtr obj(ObjectUnion::New()); + obj->set_f_array_int8(array.Pass()); + + EXPECT_EQ(8, obj->get_f_array_int8()[0]); + EXPECT_EQ(9, obj->get_f_array_int8()[1]); +} + +TEST(UnionTest, ArrayInUnionSerialization) { + Array<int8_t> array(2); + array[0] = 8; + array[1] = 9; + + ObjectUnionPtr obj(ObjectUnion::New()); + obj->set_f_array_int8(array.Pass()); + + size_t size = GetSerializedSize_(obj, false); + EXPECT_EQ(32U, size); + + mojo::internal::FixedBufferForTesting buf(size); + internal::ObjectUnion_Data* data = nullptr; + SerializeUnion_(obj.Pass(), &buf, &data, false); + + std::vector<Handle> handles; + data->EncodePointersAndHandles(&handles); + data->DecodePointersAndHandles(&handles); + + ObjectUnionPtr obj2; + Deserialize_(data, &obj2); + + EXPECT_EQ(8, obj2->get_f_array_int8()[0]); + EXPECT_EQ(9, obj2->get_f_array_int8()[1]); +} + +TEST(UnionTest, ArrayInUnionValidation) { + Array<int8_t> array(2); + array[0] = 8; + array[1] = 9; + + ObjectUnionPtr obj(ObjectUnion::New()); + obj->set_f_array_int8(array.Pass()); + + size_t size = GetSerializedSize_(obj, false); + mojo::internal::FixedBufferForTesting buf(size); + internal::ObjectUnion_Data* data = nullptr; + SerializeUnion_(obj.Pass(), &buf, &data, false); + + std::vector<Handle> handles; + data->EncodePointersAndHandles(&handles); + + void* raw_buf = buf.Leak(); + mojo::internal::BoundsChecker bounds_checker(data, + static_cast<uint32_t>(size), 0); + + EXPECT_TRUE( + internal::ObjectUnion_Data::Validate(raw_buf, &bounds_checker, false)); + free(raw_buf); +} + +TEST(UnionTest, MapInUnionGetterSetter) { + Map<String, int8_t> map; + map.insert("one", 1); + map.insert("two", 2); + + ObjectUnionPtr obj(ObjectUnion::New()); + obj->set_f_map_int8(map.Pass()); + + EXPECT_EQ(1, obj->get_f_map_int8()["one"]); + EXPECT_EQ(2, obj->get_f_map_int8()["two"]); +} + +TEST(UnionTest, MapInUnionSerialization) { + Map<String, int8_t> map; + map.insert("one", 1); + map.insert("two", 2); + + ObjectUnionPtr obj(ObjectUnion::New()); + obj->set_f_map_int8(map.Pass()); + + size_t size = GetSerializedSize_(obj, false); + EXPECT_EQ(112U, size); + + mojo::internal::FixedBufferForTesting buf(size); + internal::ObjectUnion_Data* data = nullptr; + SerializeUnion_(obj.Pass(), &buf, &data, false); + + std::vector<Handle> handles; + data->EncodePointersAndHandles(&handles); + data->DecodePointersAndHandles(&handles); + + ObjectUnionPtr obj2; + Deserialize_(data, &obj2); + + EXPECT_EQ(1, obj2->get_f_map_int8()["one"]); + EXPECT_EQ(2, obj2->get_f_map_int8()["two"]); +} + +TEST(UnionTest, MapInUnionValidation) { + Map<String, int8_t> map; + map.insert("one", 1); + map.insert("two", 2); + + ObjectUnionPtr obj(ObjectUnion::New()); + obj->set_f_map_int8(map.Pass()); + + size_t size = GetSerializedSize_(obj, false); + EXPECT_EQ(112U, size); + + mojo::internal::FixedBufferForTesting buf(size); + internal::ObjectUnion_Data* data = nullptr; + SerializeUnion_(obj.Pass(), &buf, &data, false); + + std::vector<Handle> handles; + data->EncodePointersAndHandles(&handles); + EXPECT_TRUE(handles.empty()); + + void* raw_buf = buf.Leak(); + mojo::internal::BoundsChecker bounds_checker(data, + static_cast<uint32_t>(size), 0); + + EXPECT_TRUE( + internal::ObjectUnion_Data::Validate(raw_buf, &bounds_checker, false)); + free(raw_buf); +} + +TEST(UnionTest, UnionInUnionGetterSetter) { + PodUnionPtr pod(PodUnion::New()); + pod->set_f_int8(10); + + ObjectUnionPtr obj(ObjectUnion::New()); + obj->set_f_pod_union(pod.Pass()); + + EXPECT_EQ(10, obj->get_f_pod_union()->get_f_int8()); +} + +TEST(UnionTest, UnionInUnionSerialization) { + PodUnionPtr pod(PodUnion::New()); + pod->set_f_int8(10); + + ObjectUnionPtr obj(ObjectUnion::New()); + obj->set_f_pod_union(pod.Pass()); + + size_t size = GetSerializedSize_(obj, false); + EXPECT_EQ(32U, size); + + mojo::internal::FixedBufferForTesting buf(size); + internal::ObjectUnion_Data* data = nullptr; + SerializeUnion_(obj.Pass(), &buf, &data, false); + + std::vector<Handle> handles; + data->EncodePointersAndHandles(&handles); + data->DecodePointersAndHandles(&handles); + + ObjectUnionPtr obj2; + Deserialize_(data, &obj2); + EXPECT_EQ(10, obj2->get_f_pod_union()->get_f_int8()); +} + +TEST(UnionTest, UnionInUnionValidation) { + PodUnionPtr pod(PodUnion::New()); + pod->set_f_int8(10); + + ObjectUnionPtr obj(ObjectUnion::New()); + obj->set_f_pod_union(pod.Pass()); + + size_t size = GetSerializedSize_(obj, false); + EXPECT_EQ(32U, size); + + mojo::internal::FixedBufferForTesting buf(size); + internal::ObjectUnion_Data* data = nullptr; + SerializeUnion_(obj.Pass(), &buf, &data, false); + + std::vector<Handle> handles; + data->EncodePointersAndHandles(&handles); + EXPECT_TRUE(handles.empty()); + + void* raw_buf = buf.Leak(); + mojo::internal::BoundsChecker bounds_checker(data, + static_cast<uint32_t>(size), 0); + EXPECT_TRUE( + internal::ObjectUnion_Data::Validate(raw_buf, &bounds_checker, false)); + free(raw_buf); +} + +TEST(UnionTest, UnionInUnionValidationNonNullable) { + PodUnionPtr pod(nullptr); + + ObjectUnionPtr obj(ObjectUnion::New()); + obj->set_f_pod_union(pod.Pass()); + + size_t size = GetSerializedSize_(obj, false); + + mojo::internal::FixedBufferForTesting buf(size); + internal::ObjectUnion_Data* data = nullptr; + SerializeUnion_(obj.Pass(), &buf, &data, false); + std::vector<Handle> handles; + data->EncodePointersAndHandles(&handles); + + void* raw_buf = buf.Leak(); + mojo::internal::BoundsChecker bounds_checker(data, + static_cast<uint32_t>(size), 0); + EXPECT_FALSE( + internal::ObjectUnion_Data::Validate(raw_buf, &bounds_checker, false)); + free(raw_buf); +} + +TEST(UnionTest, HandleInUnionGetterSetter) { + ScopedMessagePipeHandle pipe0; + ScopedMessagePipeHandle pipe1; + + CreateMessagePipe(nullptr, &pipe0, &pipe1); + + HandleUnionPtr handle(HandleUnion::New()); + handle->set_f_message_pipe(pipe1.Pass()); + + std::string golden("hello world"); + WriteTextMessage(pipe0.get(), golden); + + std::string actual; + ReadTextMessage(handle->get_f_message_pipe().get(), &actual); + + EXPECT_EQ(golden, actual); +} + +TEST(UnionTest, HandleInUnionSerialization) { + ScopedMessagePipeHandle pipe0; + ScopedMessagePipeHandle pipe1; + + CreateMessagePipe(nullptr, &pipe0, &pipe1); + + HandleUnionPtr handle(HandleUnion::New()); + handle->set_f_message_pipe(pipe1.Pass()); + + size_t size = GetSerializedSize_(handle, false); + EXPECT_EQ(16U, size); + + mojo::internal::FixedBufferForTesting buf(size); + internal::HandleUnion_Data* data = nullptr; + SerializeUnion_(handle.Pass(), &buf, &data, false); + + std::vector<Handle> handles; + data->EncodePointersAndHandles(&handles); + EXPECT_EQ(1U, handles.size()); + data->DecodePointersAndHandles(&handles); + + HandleUnionPtr handle2(HandleUnion::New()); + Deserialize_(data, &handle2); + + std::string golden("hello world"); + WriteTextMessage(pipe0.get(), golden); + + std::string actual; + ReadTextMessage(handle2->get_f_message_pipe().get(), &actual); + + EXPECT_EQ(golden, actual); +} + +TEST(UnionTest, HandleInUnionValidation) { + ScopedMessagePipeHandle pipe0; + ScopedMessagePipeHandle pipe1; + + CreateMessagePipe(nullptr, &pipe0, &pipe1); + + HandleUnionPtr handle(HandleUnion::New()); + handle->set_f_message_pipe(pipe1.Pass()); + + size_t size = GetSerializedSize_(handle, false); + EXPECT_EQ(16U, size); + + mojo::internal::FixedBufferForTesting buf(size); + internal::HandleUnion_Data* data = nullptr; + SerializeUnion_(handle.Pass(), &buf, &data, false); + + std::vector<Handle> handles; + data->EncodePointersAndHandles(&handles); + + void* raw_buf = buf.Leak(); + mojo::internal::BoundsChecker bounds_checker(data, + static_cast<uint32_t>(size), 1); + EXPECT_TRUE( + internal::HandleUnion_Data::Validate(raw_buf, &bounds_checker, false)); + free(raw_buf); +} + +TEST(UnionTest, HandleInUnionValidationNull) { + ScopedMessagePipeHandle pipe; + HandleUnionPtr handle(HandleUnion::New()); + handle->set_f_message_pipe(pipe.Pass()); + + size_t size = GetSerializedSize_(handle, false); + EXPECT_EQ(16U, size); + + mojo::internal::FixedBufferForTesting buf(size); + internal::HandleUnion_Data* data = nullptr; + SerializeUnion_(handle.Pass(), &buf, &data, false); + + std::vector<Handle> handles; + data->EncodePointersAndHandles(&handles); + + void* raw_buf = buf.Leak(); + mojo::internal::BoundsChecker bounds_checker(data, + static_cast<uint32_t>(size), 1); + EXPECT_FALSE( + internal::HandleUnion_Data::Validate(raw_buf, &bounds_checker, false)); + free(raw_buf); +} + +class SmallCacheImpl : public SmallCache { + public: + SmallCacheImpl() : int_value_(0) {} + ~SmallCacheImpl() override {} + int64_t int_value() const { return int_value_; } + + private: + void SetIntValue(int64_t int_value) override { int_value_ = int_value; } + void GetIntValue(const GetIntValueCallback& callback) override { + callback.Run(int_value_); + } + + int64_t int_value_; +}; + +TEST(UnionTest, InterfaceInUnion) { + base::MessageLoop run_loop(common::MessagePumpMojo::Create()); + SmallCacheImpl impl; + SmallCachePtr ptr; + Binding<SmallCache> bindings(&impl, GetProxy(&ptr)); + + HandleUnionPtr handle(HandleUnion::New()); + handle->set_f_small_cache(ptr.Pass()); + + handle->get_f_small_cache()->SetIntValue(10); + run_loop.RunUntilIdle(); + EXPECT_EQ(10, impl.int_value()); +} + +TEST(UnionTest, InterfaceInUnionSerialization) { + base::MessageLoop run_loop(common::MessagePumpMojo::Create()); + SmallCacheImpl impl; + SmallCachePtr ptr; + Binding<SmallCache> bindings(&impl, GetProxy(&ptr)); + + HandleUnionPtr handle(HandleUnion::New()); + handle->set_f_small_cache(ptr.Pass()); + size_t size = GetSerializedSize_(handle, false); + EXPECT_EQ(16U, size); + + mojo::internal::FixedBufferForTesting buf(size); + internal::HandleUnion_Data* data = nullptr; + SerializeUnion_(handle.Pass(), &buf, &data, false); + + std::vector<Handle> handles; + data->EncodePointersAndHandles(&handles); + EXPECT_EQ(1U, handles.size()); + data->DecodePointersAndHandles(&handles); + + HandleUnionPtr handle2(HandleUnion::New()); + Deserialize_(data, &handle2); + + handle2->get_f_small_cache()->SetIntValue(10); + run_loop.RunUntilIdle(); + EXPECT_EQ(10, impl.int_value()); +} + +class UnionInterfaceImpl : public UnionInterface { + public: + UnionInterfaceImpl() {} + ~UnionInterfaceImpl() override {} + + private: + void Echo(PodUnionPtr in, const EchoCallback& callback) override { + callback.Run(in.Pass()); + } +}; + +TEST(UnionTest, UnionInInterface) { + base::MessageLoop run_loop(common::MessagePumpMojo::Create()); + UnionInterfaceImpl impl; + UnionInterfacePtr ptr; + Binding<UnionInterface> bindings(&impl, GetProxy(&ptr)); + + PodUnionPtr pod(PodUnion::New()); + pod->set_f_int16(16); + + ptr->Echo(pod.Pass(), + [](PodUnionPtr out) { EXPECT_EQ(16, out->get_f_int16()); }); + run_loop.RunUntilIdle(); +} + +} // namespace test +} // namespace mojo diff --git a/mojo/public/cpp/bindings/tests/validation_test_input_parser.cc b/mojo/public/cpp/bindings/tests/validation_test_input_parser.cc new file mode 100644 index 0000000..9d2607d --- /dev/null +++ b/mojo/public/cpp/bindings/tests/validation_test_input_parser.cc @@ -0,0 +1,410 @@ +// 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. + +#include "mojo/public/cpp/bindings/tests/validation_test_input_parser.h" + +#include <assert.h> +#include <stdio.h> +#include <string.h> + +#include <limits> +#include <map> +#include <set> +#include <utility> + +#include "mojo/public/c/system/macros.h" + +namespace mojo { +namespace test { +namespace { + +class ValidationTestInputParser { + public: + ValidationTestInputParser(const std::string& input, + std::vector<uint8_t>* data, + size_t* num_handles, + std::string* error_message); + ~ValidationTestInputParser(); + + bool Run(); + + private: + struct DataType; + + typedef std::pair<const char*, const char*> Range; + + typedef bool (ValidationTestInputParser::*ParseDataFunc)( + const DataType& type, + const std::string& value_string); + + struct DataType { + const char* name; + size_t name_size; + size_t data_size; + ParseDataFunc parse_data_func; + }; + + // A dist4/8 item that hasn't been matched with an anchr item. + struct PendingDistanceItem { + // Where this data item is located in |data_|. + size_t pos; + // Either 4 or 8 (bytes). + size_t data_size; + }; + + bool GetNextItem(Range* range); + + bool ParseItem(const Range& range); + + bool ParseUnsignedInteger(const DataType& type, + const std::string& value_string); + bool ParseSignedInteger(const DataType& type, + const std::string& value_string); + bool ParseFloat(const DataType& type, const std::string& value_string); + bool ParseDouble(const DataType& type, const std::string& value_string); + bool ParseBinarySequence(const DataType& type, + const std::string& value_string); + bool ParseDistance(const DataType& type, const std::string& value_string); + bool ParseAnchor(const DataType& type, const std::string& value_string); + bool ParseHandles(const DataType& type, const std::string& value_string); + + bool StartsWith(const Range& range, const char* prefix, size_t prefix_length); + + bool ConvertToUnsignedInteger(const std::string& value_string, + unsigned long long int* value); + + template <typename T> + void AppendData(T data) { + size_t pos = data_->size(); + data_->resize(pos + sizeof(T)); + memcpy(&(*data_)[pos], &data, sizeof(T)); + } + + template <typename TargetType, typename InputType> + bool ConvertAndAppendData(InputType value) { + if (value > std::numeric_limits<TargetType>::max() || + value < std::numeric_limits<TargetType>::min()) { + return false; + } + AppendData(static_cast<TargetType>(value)); + return true; + } + + template <typename TargetType, typename InputType> + bool ConvertAndFillData(size_t pos, InputType value) { + if (value > std::numeric_limits<TargetType>::max() || + value < std::numeric_limits<TargetType>::min()) { + return false; + } + TargetType target_value = static_cast<TargetType>(value); + assert(pos + sizeof(TargetType) <= data_->size()); + memcpy(&(*data_)[pos], &target_value, sizeof(TargetType)); + return true; + } + + static const DataType kDataTypes[]; + static const size_t kDataTypeCount; + + const std::string& input_; + size_t input_cursor_; + + std::vector<uint8_t>* data_; + size_t* num_handles_; + std::string* error_message_; + + std::map<std::string, PendingDistanceItem> pending_distance_items_; + std::set<std::string> anchors_; +}; + +#define DATA_TYPE(name, data_size, parse_data_func) \ + { name, sizeof(name) - 1, data_size, parse_data_func } + +const ValidationTestInputParser::DataType + ValidationTestInputParser::kDataTypes[] = { + DATA_TYPE("[u1]", 1, &ValidationTestInputParser::ParseUnsignedInteger), + DATA_TYPE("[u2]", 2, &ValidationTestInputParser::ParseUnsignedInteger), + DATA_TYPE("[u4]", 4, &ValidationTestInputParser::ParseUnsignedInteger), + DATA_TYPE("[u8]", 8, &ValidationTestInputParser::ParseUnsignedInteger), + DATA_TYPE("[s1]", 1, &ValidationTestInputParser::ParseSignedInteger), + DATA_TYPE("[s2]", 2, &ValidationTestInputParser::ParseSignedInteger), + DATA_TYPE("[s4]", 4, &ValidationTestInputParser::ParseSignedInteger), + DATA_TYPE("[s8]", 8, &ValidationTestInputParser::ParseSignedInteger), + DATA_TYPE("[b]", 1, &ValidationTestInputParser::ParseBinarySequence), + DATA_TYPE("[f]", 4, &ValidationTestInputParser::ParseFloat), + DATA_TYPE("[d]", 8, &ValidationTestInputParser::ParseDouble), + DATA_TYPE("[dist4]", 4, &ValidationTestInputParser::ParseDistance), + DATA_TYPE("[dist8]", 8, &ValidationTestInputParser::ParseDistance), + DATA_TYPE("[anchr]", 0, &ValidationTestInputParser::ParseAnchor), + DATA_TYPE("[handles]", 0, &ValidationTestInputParser::ParseHandles)}; + +const size_t ValidationTestInputParser::kDataTypeCount = + sizeof(ValidationTestInputParser::kDataTypes) / + sizeof(ValidationTestInputParser::kDataTypes[0]); + +ValidationTestInputParser::ValidationTestInputParser(const std::string& input, + std::vector<uint8_t>* data, + size_t* num_handles, + std::string* error_message) + : input_(input), + input_cursor_(0), + data_(data), + num_handles_(num_handles), + error_message_(error_message) { + assert(data_); + assert(num_handles_); + assert(error_message_); + data_->clear(); + *num_handles_ = 0; + error_message_->clear(); +} + +ValidationTestInputParser::~ValidationTestInputParser() { +} + +bool ValidationTestInputParser::Run() { + Range range; + bool result = true; + while (result && GetNextItem(&range)) + result = ParseItem(range); + + if (!result) { + *error_message_ = + "Error occurred when parsing " + std::string(range.first, range.second); + } else if (!pending_distance_items_.empty()) { + // We have parsed all the contents in |input_| successfully, but there are + // unmatched dist4/8 items. + *error_message_ = "Error occurred when matching [dist4/8] and [anchr]."; + result = false; + } + if (!result) { + data_->clear(); + *num_handles_ = 0; + } else { + assert(error_message_->empty()); + } + + return result; +} + +bool ValidationTestInputParser::GetNextItem(Range* range) { + const char kWhitespaceChars[] = " \t\n\r"; + const char kItemDelimiters[] = " \t\n\r/"; + const char kEndOfLineChars[] = "\n\r"; + while (true) { + // Skip leading whitespaces. + // If there are no non-whitespace characters left, |input_cursor_| will be + // set to std::npos. + input_cursor_ = input_.find_first_not_of(kWhitespaceChars, input_cursor_); + + if (input_cursor_ >= input_.size()) + return false; + + if (StartsWith( + Range(&input_[0] + input_cursor_, &input_[0] + input_.size()), + "//", + 2)) { + // Skip contents until the end of the line. + input_cursor_ = input_.find_first_of(kEndOfLineChars, input_cursor_); + } else { + range->first = &input_[0] + input_cursor_; + input_cursor_ = input_.find_first_of(kItemDelimiters, input_cursor_); + range->second = input_cursor_ >= input_.size() + ? &input_[0] + input_.size() + : &input_[0] + input_cursor_; + return true; + } + } + return false; +} + +bool ValidationTestInputParser::ParseItem(const Range& range) { + for (size_t i = 0; i < kDataTypeCount; ++i) { + if (StartsWith(range, kDataTypes[i].name, kDataTypes[i].name_size)) { + return (this->*kDataTypes[i].parse_data_func)( + kDataTypes[i], + std::string(range.first + kDataTypes[i].name_size, range.second)); + } + } + + // "[u1]" is optional. + return ParseUnsignedInteger(kDataTypes[0], + std::string(range.first, range.second)); +} + +bool ValidationTestInputParser::ParseUnsignedInteger( + const DataType& type, + const std::string& value_string) { + unsigned long long int value; + if (!ConvertToUnsignedInteger(value_string, &value)) + return false; + + switch (type.data_size) { + case 1: + return ConvertAndAppendData<uint8_t>(value); + case 2: + return ConvertAndAppendData<uint16_t>(value); + case 4: + return ConvertAndAppendData<uint32_t>(value); + case 8: + return ConvertAndAppendData<uint64_t>(value); + default: + assert(false); + return false; + } +} + +bool ValidationTestInputParser::ParseSignedInteger( + const DataType& type, + const std::string& value_string) { + long long int value; + if (sscanf(value_string.c_str(), "%lli", &value) != 1) + return false; + + switch (type.data_size) { + case 1: + return ConvertAndAppendData<int8_t>(value); + case 2: + return ConvertAndAppendData<int16_t>(value); + case 4: + return ConvertAndAppendData<int32_t>(value); + case 8: + return ConvertAndAppendData<int64_t>(value); + default: + assert(false); + return false; + } +} + +bool ValidationTestInputParser::ParseFloat(const DataType& type, + const std::string& value_string) { + static_assert(sizeof(float) == 4, "sizeof(float) is not 4"); + + float value; + if (sscanf(value_string.c_str(), "%f", &value) != 1) + return false; + + AppendData(value); + return true; +} + +bool ValidationTestInputParser::ParseDouble(const DataType& type, + const std::string& value_string) { + static_assert(sizeof(double) == 8, "sizeof(double) is not 8"); + + double value; + if (sscanf(value_string.c_str(), "%lf", &value) != 1) + return false; + + AppendData(value); + return true; +} + +bool ValidationTestInputParser::ParseBinarySequence( + const DataType& type, + const std::string& value_string) { + if (value_string.size() != 8) + return false; + + uint8_t value = 0; + for (std::string::const_iterator iter = value_string.begin(); + iter != value_string.end(); + ++iter) { + value <<= 1; + if (*iter == '1') + value++; + else if (*iter != '0') + return false; + } + AppendData(value); + return true; +} + +bool ValidationTestInputParser::ParseDistance(const DataType& type, + const std::string& value_string) { + if (pending_distance_items_.find(value_string) != + pending_distance_items_.end()) + return false; + + PendingDistanceItem item = {data_->size(), type.data_size}; + data_->resize(data_->size() + type.data_size); + pending_distance_items_[value_string] = item; + + return true; +} + +bool ValidationTestInputParser::ParseAnchor(const DataType& type, + const std::string& value_string) { + if (anchors_.find(value_string) != anchors_.end()) + return false; + anchors_.insert(value_string); + + std::map<std::string, PendingDistanceItem>::iterator iter = + pending_distance_items_.find(value_string); + if (iter == pending_distance_items_.end()) + return false; + + PendingDistanceItem dist_item = iter->second; + pending_distance_items_.erase(iter); + + size_t distance = data_->size() - dist_item.pos; + switch (dist_item.data_size) { + case 4: + return ConvertAndFillData<uint32_t>(dist_item.pos, distance); + case 8: + return ConvertAndFillData<uint64_t>(dist_item.pos, distance); + default: + assert(false); + return false; + } +} + +bool ValidationTestInputParser::ParseHandles(const DataType& type, + const std::string& value_string) { + // It should be the first item. + if (!data_->empty()) + return false; + + unsigned long long int value; + if (!ConvertToUnsignedInteger(value_string, &value)) + return false; + + if (value > std::numeric_limits<size_t>::max()) + return false; + + *num_handles_ = static_cast<size_t>(value); + return true; +} + +bool ValidationTestInputParser::StartsWith(const Range& range, + const char* prefix, + size_t prefix_length) { + if (static_cast<size_t>(range.second - range.first) < prefix_length) + return false; + + return memcmp(range.first, prefix, prefix_length) == 0; +} + +bool ValidationTestInputParser::ConvertToUnsignedInteger( + const std::string& value_string, + unsigned long long int* value) { + const char* format = nullptr; + if (value_string.find_first_of("xX") != std::string::npos) + format = "%llx"; + else + format = "%llu"; + return sscanf(value_string.c_str(), format, value) == 1; +} + +} // namespace + +bool ParseValidationTestInput(const std::string& input, + std::vector<uint8_t>* data, + size_t* num_handles, + std::string* error_message) { + ValidationTestInputParser parser(input, data, num_handles, error_message); + return parser.Run(); +} + +} // namespace test +} // namespace mojo diff --git a/mojo/public/cpp/bindings/tests/validation_test_input_parser.h b/mojo/public/cpp/bindings/tests/validation_test_input_parser.h new file mode 100644 index 0000000..d5f8c81 --- /dev/null +++ b/mojo/public/cpp/bindings/tests/validation_test_input_parser.h @@ -0,0 +1,120 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_TESTS_VALIDATION_TEST_INPUT_PARSER_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_TESTS_VALIDATION_TEST_INPUT_PARSER_H_ + +#include <stdint.h> + +#include <string> +#include <vector> + +namespace mojo { +namespace test { + +// Input Format of Mojo Message Validation Tests. +// +// Data items are separated by whitespaces: +// - ' ' (0x20) space; +// - '\t' (0x09) horizontal tab; +// - '\n' (0x0a) newline; +// - '\r' (0x0d) carriage return. +// A comment starts with //, extending to the end of the line. +// Each data item is of the format [<type>]<value>. The types defined and the +// corresponding value formats are described below. +// +// Type: u1 / u2 / u4 / u8 +// Description: Little-endian 1/2/4/8-byte unsigned integer. +// Value Format: +// - Decimal integer: 0|[1-9][0-9]* +// - Hexadecimal integer: 0[xX][0-9a-fA-F]+ +// - The type prefix (including the square brackets) of 1-byte unsigned +// integer is optional. +// +// Type: s1 / s2 / s4 / s8 +// Description: Little-endian 1/2/4/8-byte signed integer. +// Value Format: +// - Decimal integer: [-+]?(0|[1-9][0-9]*) +// - Hexadecimal integer: [-+]?0[xX][0-9a-fA-F]+ +// +// Type: b +// Description: Binary sequence of 1 byte. +// Value Format: [01]{8} +// +// Type: f / d +// Description: Little-endian IEEE-754 format of float (4 bytes) and double (8 +// bytes). +// Value Format: [-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)? +// +// Type: dist4 / dist8 +// Description: Little-endian 4/8-byte unsigned integer. The actual value is set +// to the byte distance from the location of this integer to the location of the +// anchr item with the same ID. A dist8 and anchr pair can be used to easily +// represent an encoded pointer. A dist4 and anchr pair can be used to easily +// calculate struct/array size. +// Value Format: The value is an ID: [0-9a-zA-Z_]+ +// +// Type: anchr +// Description: Mark an anchor location. It doesn’t translate into any actual +// data. +// Value Format: The value is an ID of the same format as that of dist4/8. +// +// Type: handles +// Description: The number of handles that are associated with the message. This +// special item is not part of the message data. If specified, it should be the +// first item. +// Value Format: The same format as u1/2/4/8. +// +// EXAMPLE: +// +// Suppose you have the following Mojo types defined: +// struct Bar { +// int32 a; +// bool b; +// bool c; +// }; +// struct Foo { +// Bar x; +// uint32 y; +// }; +// +// The following describes a valid message whose payload is a Foo struct: +// // message header +// [dist4]message_header // num_bytes +// [u4]3 // version +// [u4]0 // type +// [u4]1 // flags +// [u8]1234 // request_id +// [anchr]message_header +// +// // payload +// [dist4]foo // num_bytes +// [u4]2 // version +// [dist8]bar_ptr // x +// [u4]0xABCD // y +// [u4]0 // padding +// [anchr]foo +// +// [anchr]bar_ptr +// [dist4]bar // num_bytes +// [u4]3 // version +// [s4]-1 // a +// [b]00000010 // b and c +// 0 0 0 // padding +// [anchr]bar + +// Parses validation test input. +// On success, |data| and |num_handles| store the parsing result, +// |error_message| is cleared; on failure, |error_message| is set to a message +// describing the error, |data| is cleared and |num_handles| set to 0. +// Note: For now, this method only works on little-endian platforms. +bool ParseValidationTestInput(const std::string& input, + std::vector<uint8_t>* data, + size_t* num_handles, + std::string* error_message); + +} // namespace test +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_TESTS_VALIDATION_TEST_INPUT_PARSER_H_ diff --git a/mojo/public/cpp/bindings/tests/validation_unittest.cc b/mojo/public/cpp/bindings/tests/validation_unittest.cc new file mode 100644 index 0000000..fc536c2 --- /dev/null +++ b/mojo/public/cpp/bindings/tests/validation_unittest.cc @@ -0,0 +1,480 @@ +// 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. + +#include <stdio.h> + +#include <algorithm> +#include <string> +#include <vector> + +#include "base/message_loop/message_loop.h" +#include "mojo/message_pump/message_pump_mojo.h" +#include "mojo/public/c/system/macros.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/interface_ptr.h" +#include "mojo/public/cpp/bindings/lib/connector.h" +#include "mojo/public/cpp/bindings/lib/filter_chain.h" +#include "mojo/public/cpp/bindings/lib/message_header_validator.h" +#include "mojo/public/cpp/bindings/lib/router.h" +#include "mojo/public/cpp/bindings/lib/validation_errors.h" +#include "mojo/public/cpp/bindings/message.h" +#include "mojo/public/cpp/bindings/tests/validation_test_input_parser.h" +#include "mojo/public/cpp/system/core.h" +#include "mojo/public/cpp/test_support/test_support.h" +#include "mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace test { +namespace { + +template <typename T> +void Append(std::vector<uint8_t>* data_vector, T data) { + size_t pos = data_vector->size(); + data_vector->resize(pos + sizeof(T)); + memcpy(&(*data_vector)[pos], &data, sizeof(T)); +} + +bool TestInputParser(const std::string& input, + bool expected_result, + const std::vector<uint8_t>& expected_data, + size_t expected_num_handles) { + std::vector<uint8_t> data; + size_t num_handles; + std::string error_message; + + bool result = + ParseValidationTestInput(input, &data, &num_handles, &error_message); + if (expected_result) { + if (result && error_message.empty() && expected_data == data && + expected_num_handles == num_handles) { + return true; + } + + // Compare with an empty string instead of checking |error_message.empty()|, + // so that the message will be printed out if the two are not equal. + EXPECT_EQ(std::string(), error_message); + EXPECT_EQ(expected_data, data); + EXPECT_EQ(expected_num_handles, num_handles); + return false; + } + + EXPECT_FALSE(error_message.empty()); + return !result && !error_message.empty(); +} + +std::vector<std::string> GetMatchingTests(const std::vector<std::string>& names, + const std::string& prefix) { + const std::string suffix = ".data"; + std::vector<std::string> tests; + for (size_t i = 0; i < names.size(); ++i) { + if (names[i].size() >= suffix.size() && + names[i].substr(0, prefix.size()) == prefix && + names[i].substr(names[i].size() - suffix.size()) == suffix) + tests.push_back(names[i].substr(0, names[i].size() - suffix.size())); + } + return tests; +} + +bool ReadFile(const std::string& path, std::string* result) { + FILE* fp = OpenSourceRootRelativeFile(path.c_str()); + if (!fp) { + ADD_FAILURE() << "File not found: " << path; + return false; + } + fseek(fp, 0, SEEK_END); + size_t size = static_cast<size_t>(ftell(fp)); + if (size == 0) { + result->clear(); + fclose(fp); + return true; + } + fseek(fp, 0, SEEK_SET); + result->resize(size); + size_t size_read = fread(&result->at(0), 1, size, fp); + fclose(fp); + return size == size_read; +} + +bool ReadAndParseDataFile(const std::string& path, + std::vector<uint8_t>* data, + size_t* num_handles) { + std::string input; + if (!ReadFile(path, &input)) + return false; + + std::string error_message; + if (!ParseValidationTestInput(input, data, num_handles, &error_message)) { + ADD_FAILURE() << error_message; + return false; + } + + return true; +} + +bool ReadResultFile(const std::string& path, std::string* result) { + if (!ReadFile(path, result)) + return false; + + // Result files are new-line delimited text files. Remove any CRs. + result->erase(std::remove(result->begin(), result->end(), '\r'), + result->end()); + + // Remove trailing LFs. + size_t pos = result->find_last_not_of('\n'); + if (pos == std::string::npos) + result->clear(); + else + result->resize(pos + 1); + + return true; +} + +std::string GetPath(const std::string& root, const std::string& suffix) { + return "mojo/public/interfaces/bindings/tests/data/validation/" + root + + suffix; +} + +// |message| should be a newly created object. +bool ReadTestCase(const std::string& test, + Message* message, + std::string* expected) { + std::vector<uint8_t> data; + size_t num_handles; + if (!ReadAndParseDataFile(GetPath(test, ".data"), &data, &num_handles) || + !ReadResultFile(GetPath(test, ".expected"), expected)) { + return false; + } + + message->AllocUninitializedData(static_cast<uint32_t>(data.size())); + if (!data.empty()) + memcpy(message->mutable_data(), &data[0], data.size()); + message->mutable_handles()->resize(num_handles); + + return true; +} + +void RunValidationTests(const std::string& prefix, + MessageReceiver* test_message_receiver) { + std::vector<std::string> names = + EnumerateSourceRootRelativeDirectory(GetPath("", "")); + std::vector<std::string> tests = GetMatchingTests(names, prefix); + + for (size_t i = 0; i < tests.size(); ++i) { + Message message; + std::string expected; + ASSERT_TRUE(ReadTestCase(tests[i], &message, &expected)); + + std::string result; + mojo::internal::ValidationErrorObserverForTesting observer; + mojo_ignore_result(test_message_receiver->Accept(&message)); + if (observer.last_error() == mojo::internal::VALIDATION_ERROR_NONE) + result = "PASS"; + else + result = mojo::internal::ValidationErrorToString(observer.last_error()); + + EXPECT_EQ(expected, result) << "failed test: " << tests[i]; + } +} + +class DummyMessageReceiver : public MessageReceiver { + public: + bool Accept(Message* message) override { + return true; // Any message is OK. + } +}; + +using ValidationTest = testing::Test; + +class ValidationIntegrationTest : public ValidationTest { + public: + ValidationIntegrationTest() + : loop_(common::MessagePumpMojo::Create()), + test_message_receiver_(nullptr) {} + + ~ValidationIntegrationTest() override {} + + void SetUp() override { + ScopedMessagePipeHandle tester_endpoint; + ASSERT_EQ(MOJO_RESULT_OK, + CreateMessagePipe(nullptr, &tester_endpoint, &testee_endpoint_)); + test_message_receiver_ = + new TestMessageReceiver(this, tester_endpoint.Pass()); + } + + void TearDown() override { + delete test_message_receiver_; + test_message_receiver_ = nullptr; + + // Make sure that the other end receives the OnConnectionError() + // notification. + PumpMessages(); + } + + MessageReceiver* test_message_receiver() { return test_message_receiver_; } + + ScopedMessagePipeHandle testee_endpoint() { return testee_endpoint_.Pass(); } + + private: + class TestMessageReceiver : public MessageReceiver { + public: + TestMessageReceiver(ValidationIntegrationTest* owner, + ScopedMessagePipeHandle handle) + : owner_(owner), connector_(handle.Pass()) { + connector_.set_enforce_errors_from_incoming_receiver(false); + } + ~TestMessageReceiver() override {} + + bool Accept(Message* message) override { + bool rv = connector_.Accept(message); + owner_->PumpMessages(); + return rv; + } + + public: + ValidationIntegrationTest* owner_; + mojo::internal::Connector connector_; + }; + + void PumpMessages() { loop_.RunUntilIdle(); } + + base::MessageLoop loop_; + TestMessageReceiver* test_message_receiver_; + ScopedMessagePipeHandle testee_endpoint_; +}; + +class IntegrationTestInterfaceImpl : public IntegrationTestInterface { + public: + ~IntegrationTestInterfaceImpl() override {} + + void Method0(BasicStructPtr param0, + const Method0Callback& callback) override { + callback.Run(Array<uint8_t>::New(0u)); + } +}; + +TEST_F(ValidationTest, InputParser) { + { + // The parser, as well as Append() defined above, assumes that this code is + // running on a little-endian platform. Test whether that is true. + uint16_t x = 1; + ASSERT_EQ(1, *(reinterpret_cast<char*>(&x))); + } + { + // Test empty input. + std::string input; + std::vector<uint8_t> expected; + + EXPECT_TRUE(TestInputParser(input, true, expected, 0)); + } + { + // Test input that only consists of comments and whitespaces. + std::string input = " \t // hello world \n\r \t// the answer is 42 "; + std::vector<uint8_t> expected; + + EXPECT_TRUE(TestInputParser(input, true, expected, 0)); + } + { + std::string input = + "[u1]0x10// hello world !! \n\r \t [u2]65535 \n" + "[u4]65536 [u8]0xFFFFFFFFFFFFFFFF 0 0Xff"; + std::vector<uint8_t> expected; + Append(&expected, static_cast<uint8_t>(0x10)); + Append(&expected, static_cast<uint16_t>(65535)); + Append(&expected, static_cast<uint32_t>(65536)); + Append(&expected, static_cast<uint64_t>(0xffffffffffffffff)); + Append(&expected, static_cast<uint8_t>(0)); + Append(&expected, static_cast<uint8_t>(0xff)); + + EXPECT_TRUE(TestInputParser(input, true, expected, 0)); + } + { + std::string input = "[s8]-0x800 [s1]-128\t[s2]+0 [s4]-40"; + std::vector<uint8_t> expected; + Append(&expected, -static_cast<int64_t>(0x800)); + Append(&expected, static_cast<int8_t>(-128)); + Append(&expected, static_cast<int16_t>(0)); + Append(&expected, static_cast<int32_t>(-40)); + + EXPECT_TRUE(TestInputParser(input, true, expected, 0)); + } + { + std::string input = "[b]00001011 [b]10000000 // hello world\r [b]00000000"; + std::vector<uint8_t> expected; + Append(&expected, static_cast<uint8_t>(11)); + Append(&expected, static_cast<uint8_t>(128)); + Append(&expected, static_cast<uint8_t>(0)); + + EXPECT_TRUE(TestInputParser(input, true, expected, 0)); + } + { + std::string input = "[f]+.3e9 [d]-10.03"; + std::vector<uint8_t> expected; + Append(&expected, +.3e9f); + Append(&expected, -10.03); + + EXPECT_TRUE(TestInputParser(input, true, expected, 0)); + } + { + std::string input = "[dist4]foo 0 [dist8]bar 0 [anchr]foo [anchr]bar"; + std::vector<uint8_t> expected; + Append(&expected, static_cast<uint32_t>(14)); + Append(&expected, static_cast<uint8_t>(0)); + Append(&expected, static_cast<uint64_t>(9)); + Append(&expected, static_cast<uint8_t>(0)); + + EXPECT_TRUE(TestInputParser(input, true, expected, 0)); + } + { + std::string input = "// This message has handles! \n[handles]50 [u8]2"; + std::vector<uint8_t> expected; + Append(&expected, static_cast<uint64_t>(2)); + + EXPECT_TRUE(TestInputParser(input, true, expected, 50)); + } + + // Test some failure cases. + { + const char* error_inputs[] = {"/ hello world", + "[u1]x", + "[u2]-1000", + "[u1]0x100", + "[s2]-0x8001", + "[b]1", + "[b]1111111k", + "[dist4]unmatched", + "[anchr]hello [dist8]hello", + "[dist4]a [dist4]a [anchr]a", + "[dist4]a [anchr]a [dist4]a [anchr]a", + "0 [handles]50", + nullptr}; + + for (size_t i = 0; error_inputs[i]; ++i) { + std::vector<uint8_t> expected; + if (!TestInputParser(error_inputs[i], false, expected, 0)) + ADD_FAILURE() << "Unexpected test result for: " << error_inputs[i]; + } + } +} + +TEST_F(ValidationTest, Conformance) { + DummyMessageReceiver dummy_receiver; + mojo::internal::FilterChain validators(&dummy_receiver); + validators.Append<mojo::internal::MessageHeaderValidator>(); + validators.Append<ConformanceTestInterface::RequestValidator_>(); + + RunValidationTests("conformance_", validators.GetHead()); +} + +// This test is similar to Conformance test but its goal is specifically +// do bounds-check testing of message validation. For example we test the +// detection of off-by-one errors in method ordinals. +TEST_F(ValidationTest, BoundsCheck) { + DummyMessageReceiver dummy_receiver; + mojo::internal::FilterChain validators(&dummy_receiver); + validators.Append<mojo::internal::MessageHeaderValidator>(); + validators.Append<BoundsCheckTestInterface::RequestValidator_>(); + + RunValidationTests("boundscheck_", validators.GetHead()); +} + +// This test is similar to the Conformance test but for responses. +TEST_F(ValidationTest, ResponseConformance) { + DummyMessageReceiver dummy_receiver; + mojo::internal::FilterChain validators(&dummy_receiver); + validators.Append<mojo::internal::MessageHeaderValidator>(); + validators.Append<ConformanceTestInterface::ResponseValidator_>(); + + RunValidationTests("resp_conformance_", validators.GetHead()); +} + +// This test is similar to the BoundsCheck test but for responses. +TEST_F(ValidationTest, ResponseBoundsCheck) { + DummyMessageReceiver dummy_receiver; + mojo::internal::FilterChain validators(&dummy_receiver); + validators.Append<mojo::internal::MessageHeaderValidator>(); + validators.Append<BoundsCheckTestInterface::ResponseValidator_>(); + + RunValidationTests("resp_boundscheck_", validators.GetHead()); +} + +// Test that InterfacePtr<X> applies the correct validators and they don't +// conflict with each other: +// - MessageHeaderValidator +// - X::ResponseValidator_ +TEST_F(ValidationIntegrationTest, InterfacePtr) { + IntegrationTestInterfacePtr interface_ptr = MakeProxy( + InterfacePtrInfo<IntegrationTestInterface>(testee_endpoint().Pass(), 0u)); + interface_ptr.internal_state()->router_for_testing()->EnableTestingMode(); + + RunValidationTests("integration_intf_resp", test_message_receiver()); + RunValidationTests("integration_msghdr", test_message_receiver()); +} + +// Test that Binding<X> applies the correct validators and they don't +// conflict with each other: +// - MessageHeaderValidator +// - X::RequestValidator_ +TEST_F(ValidationIntegrationTest, Binding) { + IntegrationTestInterfaceImpl interface_impl; + Binding<IntegrationTestInterface> binding( + &interface_impl, + MakeRequest<IntegrationTestInterface>(testee_endpoint().Pass())); + binding.internal_router()->EnableTestingMode(); + + RunValidationTests("integration_intf_rqst", test_message_receiver()); + RunValidationTests("integration_msghdr", test_message_receiver()); +} + +// Test pointer validation (specifically, that the encoded offset is 32-bit) +TEST_F(ValidationTest, ValidateEncodedPointer) { + uint64_t offset; + + offset = 0ULL; + EXPECT_TRUE(mojo::internal::ValidateEncodedPointer(&offset)); + + offset = 1ULL; + EXPECT_TRUE(mojo::internal::ValidateEncodedPointer(&offset)); + + // offset must be <= 32-bit. + offset = std::numeric_limits<uint32_t>::max() + 1ULL; + EXPECT_FALSE(mojo::internal::ValidateEncodedPointer(&offset)); +} + +// Tests the IsValidValue() function generated for BasicEnum. +TEST(EnumValueValidationTest, BasicEnum) { + // BasicEnum can have -3,0,1,10 as possible integral values. + EXPECT_FALSE(BasicEnum_IsValidValue(static_cast<BasicEnum>(-4))); + EXPECT_TRUE(BasicEnum_IsValidValue(static_cast<BasicEnum>(-3))); + EXPECT_FALSE(BasicEnum_IsValidValue(static_cast<BasicEnum>(-2))); + EXPECT_FALSE(BasicEnum_IsValidValue(static_cast<BasicEnum>(-1))); + EXPECT_TRUE(BasicEnum_IsValidValue(static_cast<BasicEnum>(0))); + EXPECT_TRUE(BasicEnum_IsValidValue(static_cast<BasicEnum>(1))); + EXPECT_FALSE(BasicEnum_IsValidValue(static_cast<BasicEnum>(2))); + EXPECT_FALSE(BasicEnum_IsValidValue(static_cast<BasicEnum>(9))); + // In the mojom, we represent this value as hex (0xa). + EXPECT_TRUE(BasicEnum_IsValidValue(static_cast<BasicEnum>(10))); + EXPECT_FALSE(BasicEnum_IsValidValue(static_cast<BasicEnum>(11))); +} + +// Tests the IsValidValue() method generated for StructWithEnum. +TEST(EnumValueValidationTest, EnumWithin) { + // StructWithEnum::EnumWithin can have [0,4] as possible integral values. + EXPECT_FALSE(StructWithEnum::EnumWithin_IsValidValue( + static_cast<StructWithEnum::EnumWithin>(-1))); + EXPECT_TRUE(StructWithEnum::EnumWithin_IsValidValue( + static_cast<StructWithEnum::EnumWithin>(0))); + EXPECT_TRUE(StructWithEnum::EnumWithin_IsValidValue( + static_cast<StructWithEnum::EnumWithin>(1))); + EXPECT_TRUE(StructWithEnum::EnumWithin_IsValidValue( + static_cast<StructWithEnum::EnumWithin>(2))); + EXPECT_TRUE(StructWithEnum::EnumWithin_IsValidValue( + static_cast<StructWithEnum::EnumWithin>(3))); + EXPECT_FALSE(StructWithEnum::EnumWithin_IsValidValue( + static_cast<StructWithEnum::EnumWithin>(4))); +} + +} // namespace +} // namespace test +} // namespace mojo diff --git a/mojo/public/cpp/bindings/tests/versioning_apptest.cc b/mojo/public/cpp/bindings/tests/versioning_apptest.cc new file mode 100644 index 0000000..a1cb37f --- /dev/null +++ b/mojo/public/cpp/bindings/tests/versioning_apptest.cc @@ -0,0 +1,121 @@ +// Copyright 2015 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. + +#include "mojo/application/public/cpp/application_impl.h" +#include "mojo/application/public/cpp/application_test_base.h" +#include "mojo/public/cpp/system/macros.h" +#include "mojo/public/interfaces/bindings/tests/versioning_test_client.mojom.h" + +namespace mojo { +namespace test { +namespace versioning { + +class VersioningApplicationTest : public ApplicationTestBase { + public: + VersioningApplicationTest() : ApplicationTestBase() {} + ~VersioningApplicationTest() override {} + + protected: + // ApplicationTestBase overrides. + void SetUp() override { + ApplicationTestBase::SetUp(); + + application_impl()->ConnectToService("mojo:versioning_test_service", + &database_); + } + + HumanResourceDatabasePtr database_; + + private: + MOJO_DISALLOW_COPY_AND_ASSIGN(VersioningApplicationTest); +}; + +TEST_F(VersioningApplicationTest, Struct) { + // The service side uses a newer version of Employee defintion. + // The returned struct should be truncated. + EmployeePtr employee(Employee::New()); + employee->employee_id = 1; + employee->name = "Homer Simpson"; + employee->department = DEPARTMENT_DEV; + + database_->QueryEmployee(1, true, + [&employee](EmployeePtr returned_employee, + Array<uint8_t> returned_finger_print) { + EXPECT_TRUE(employee->Equals(*returned_employee)); + EXPECT_FALSE(returned_finger_print.is_null()); + }); + database_.WaitForIncomingResponse(); + + // Passing a struct of older version to the service side works. + EmployeePtr new_employee(Employee::New()); + new_employee->employee_id = 2; + new_employee->name = "Marge Simpson"; + new_employee->department = DEPARTMENT_SALES; + + database_->AddEmployee(new_employee.Clone(), + [](bool success) { EXPECT_TRUE(success); }); + database_.WaitForIncomingResponse(); + + database_->QueryEmployee( + 2, false, [&new_employee](EmployeePtr returned_employee, + Array<uint8_t> returned_finger_print) { + EXPECT_TRUE(new_employee->Equals(*returned_employee)); + EXPECT_TRUE(returned_finger_print.is_null()); + }); + database_.WaitForIncomingResponse(); +} + +TEST_F(VersioningApplicationTest, QueryVersion) { + EXPECT_EQ(0u, database_.version()); + database_.QueryVersion([](uint32_t version) { EXPECT_EQ(1u, version); }); + database_.WaitForIncomingResponse(); + EXPECT_EQ(1u, database_.version()); +} + +TEST_F(VersioningApplicationTest, RequireVersion) { + EXPECT_EQ(0u, database_.version()); + + database_.RequireVersion(1); + EXPECT_EQ(1u, database_.version()); + database_->QueryEmployee(3, false, + [](EmployeePtr returned_employee, + Array<uint8_t> returned_finger_print) {}); + database_.WaitForIncomingResponse(); + EXPECT_FALSE(database_.encountered_error()); + + // Requiring a version higher than what the service side implements will close + // the pipe. + database_.RequireVersion(3); + EXPECT_EQ(3u, database_.version()); + database_->QueryEmployee(1, false, + [](EmployeePtr returned_employee, + Array<uint8_t> returned_finger_print) {}); + database_.WaitForIncomingResponse(); + EXPECT_TRUE(database_.encountered_error()); +} + +TEST_F(VersioningApplicationTest, CallNonexistentMethod) { + EXPECT_EQ(0u, database_.version()); + + Array<uint8_t> new_finger_print(128); + for (size_t i = 0; i < 128; ++i) + new_finger_print[i] = i + 13; + + // Although the client side doesn't know whether the service side supports + // version 1, calling a version 1 method succeeds as long as the service side + // supports version 1. + database_->AttachFingerPrint(1, new_finger_print.Clone(), + [](bool success) { EXPECT_TRUE(success); }); + database_.WaitForIncomingResponse(); + + // Calling a version 2 method (which the service side doesn't support) closes + // the pipe. + database_->ListEmployeeIds([](Array<uint64_t> ids) { EXPECT_TRUE(false); }); + database_.WaitForIncomingResponse(); + EXPECT_TRUE(database_.encountered_error()); +} + +} // namespace versioning +} // namespace examples +} // namespace mojo diff --git a/mojo/public/cpp/bindings/tests/versioning_test_service.cc b/mojo/public/cpp/bindings/tests/versioning_test_service.cc new file mode 100644 index 0000000..cc52595 --- /dev/null +++ b/mojo/public/cpp/bindings/tests/versioning_test_service.cc @@ -0,0 +1,125 @@ +// Copyright 2015 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. + +#include <map> + +#include "mojo/application/public/cpp/application_connection.h" +#include "mojo/application/public/cpp/application_delegate.h" +#include "mojo/application/public/cpp/application_runner.h" +#include "mojo/application/public/cpp/interface_factory.h" +#include "mojo/public/c/system/main.h" +#include "mojo/public/cpp/bindings/strong_binding.h" +#include "mojo/public/cpp/system/macros.h" +#include "mojo/public/interfaces/bindings/tests/versioning_test_service.mojom.h" + +namespace mojo { +namespace test { +namespace versioning { + +struct EmployeeInfo { + public: + EmployeeInfo() {} + + EmployeePtr employee; + Array<uint8_t> finger_print; + + private: + MOJO_DISALLOW_COPY_AND_ASSIGN(EmployeeInfo); +}; + +class HumanResourceDatabaseImpl : public HumanResourceDatabase { + public: + explicit HumanResourceDatabaseImpl( + InterfaceRequest<HumanResourceDatabase> request) + : strong_binding_(this, request.Pass()) { + // Pretend that there is already some data in the system. + EmployeeInfo* info = new EmployeeInfo(); + employees_[1] = info; + info->employee = Employee::New(); + info->employee->employee_id = 1; + info->employee->name = "Homer Simpson"; + info->employee->department = DEPARTMENT_DEV; + info->employee->birthday = Date::New(); + info->employee->birthday->year = 1955; + info->employee->birthday->month = 5; + info->employee->birthday->day = 12; + info->finger_print.resize(1024); + for (uint32_t i = 0; i < 1024; ++i) + info->finger_print[i] = i; + } + + ~HumanResourceDatabaseImpl() override { + for (auto iter = employees_.begin(); iter != employees_.end(); ++iter) + delete iter->second; + } + + void AddEmployee(EmployeePtr employee, + const AddEmployeeCallback& callback) override { + uint64_t id = employee->employee_id; + if (employees_.find(id) == employees_.end()) + employees_[id] = new EmployeeInfo(); + employees_[id]->employee = employee.Pass(); + callback.Run(true); + } + + void QueryEmployee(uint64_t id, + bool retrieve_finger_print, + const QueryEmployeeCallback& callback) override { + if (employees_.find(id) == employees_.end()) { + callback.Run(nullptr, Array<uint8_t>()); + return; + } + callback.Run(employees_[id]->employee.Clone(), + retrieve_finger_print ? employees_[id]->finger_print.Clone() + : Array<uint8_t>()); + } + + void AttachFingerPrint(uint64_t id, + Array<uint8_t> finger_print, + const AttachFingerPrintCallback& callback) override { + if (employees_.find(id) == employees_.end()) { + callback.Run(false); + return; + } + employees_[id]->finger_print = finger_print.Pass(); + callback.Run(true); + } + + private: + std::map<uint64_t, EmployeeInfo*> employees_; + + StrongBinding<HumanResourceDatabase> strong_binding_; +}; + +class HumanResourceSystemServer + : public ApplicationDelegate, + public InterfaceFactory<HumanResourceDatabase> { + public: + HumanResourceSystemServer() {} + + // ApplicationDelegate implementation. + bool ConfigureIncomingConnection(ApplicationConnection* connection) override { + connection->AddService<HumanResourceDatabase>(this); + return true; + } + + // InterfaceFactory<HumanResourceDatabase> implementation. + void Create(ApplicationConnection* connection, + InterfaceRequest<HumanResourceDatabase> request) override { + // It will be deleted automatically when the underlying pipe encounters a + // connection error. + new HumanResourceDatabaseImpl(request.Pass()); + } +}; + +} // namespace versioning +} // namespace test +} // namespace mojo + +MojoResult MojoMain(MojoHandle application_request) { + mojo::ApplicationRunner runner( + new mojo::test::versioning::HumanResourceSystemServer()); + + return runner.Run(application_request); +} diff --git a/mojo/public/cpp/bindings/type_converter.h b/mojo/public/cpp/bindings/type_converter.h new file mode 100644 index 0000000..ff94cda --- /dev/null +++ b/mojo/public/cpp/bindings/type_converter.h @@ -0,0 +1,92 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_TYPE_CONVERTER_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_TYPE_CONVERTER_H_ + +namespace mojo { + +// Specialize the following class: +// template <typename T, typename U> struct TypeConverter; +// to perform type conversion for Mojom-defined structs and arrays. Here, T is +// the target type; U is the input type. +// +// Specializations should implement the following interfaces: +// namespace mojo { +// template <> +// struct TypeConverter<X, Y> { +// static X Convert(const Y& input); +// }; +// template <> +// struct TypeConverter<Y, X> { +// static Y Convert(const X& input); +// }; +// } +// +// EXAMPLE: +// +// Suppose you have the following Mojom-defined struct: +// +// module geometry { +// struct Point { +// int32 x; +// int32 y; +// }; +// } +// +// Now, imagine you wanted to write a TypeConverter specialization for +// gfx::Point. It might look like this: +// +// namespace mojo { +// template <> +// struct TypeConverter<geometry::PointPtr, gfx::Point> { +// static geometry::PointPtr Convert(const gfx::Point& input) { +// geometry::PointPtr result; +// result->x = input.x(); +// result->y = input.y(); +// return result.Pass(); +// } +// }; +// template <> +// struct TypeConverter<gfx::Point, geometry::PointPtr> { +// static gfx::Point Convert(const geometry::PointPtr& input) { +// return input ? gfx::Point(input->x, input->y) : gfx::Point(); +// } +// }; +// } +// +// With the above TypeConverter defined, it is possible to write code like this: +// +// void AcceptPoint(const geometry::PointPtr& input) { +// // With an explicit cast using the .To<> method. +// gfx::Point pt = input.To<gfx::Point>(); +// +// // With an explicit cast using the static From() method. +// geometry::PointPtr output = geometry::Point::From(pt); +// +// // Inferring the input type using the ConvertTo helper function. +// gfx::Point pt2 = ConvertTo<gfx::Point>(input); +// } +// +template <typename T, typename U> +struct TypeConverter; + +// The following specialization is useful when you are converting between +// Array<POD> and std::vector<POD>. +template <typename T> +struct TypeConverter<T, T> { + static T Convert(const T& obj) { return obj; } +}; + +// The following helper function is useful for shorthand. The compiler can infer +// the input type, so you can write: +// OutputType out = ConvertTo<OutputType>(input); +template <typename T, typename U> +inline T ConvertTo(const U& obj) { + return TypeConverter<T, U>::Convert(obj); +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_TYPE_CONVERTER_H_ diff --git a/mojo/public/cpp/environment/BUILD.gn b/mojo/public/cpp/environment/BUILD.gn new file mode 100644 index 0000000..fe3a011 --- /dev/null +++ b/mojo/public/cpp/environment/BUILD.gn @@ -0,0 +1,47 @@ +# 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. + +import("../../mojo_sdk.gni") + +mojo_sdk_source_set("environment") { + sources = [ + "async_waiter.h", + "environment.h", + "logging.h", + "task_tracker.h", + ] + + mojo_sdk_public_deps = [ "mojo/public/c/environment" ] + + mojo_sdk_deps = [ + "mojo/public/cpp/bindings:callback", + "mojo/public/cpp/system", + ] +} + +mojo_sdk_source_set("standalone") { + sources = [ + "lib/async_waiter.cc", + "lib/default_async_waiter.cc", + "lib/default_async_waiter.h", + "lib/default_logger.cc", + "lib/default_logger.h", + "lib/default_task_tracker.cc", + "lib/default_task_tracker.h", + "lib/environment.cc", + "lib/logging.cc", + "lib/scoped_task_tracking.cc", + "lib/scoped_task_tracking.h", + ] + + public_deps = [ + ":environment", + ] + + mojo_sdk_deps = [ + "mojo/public/c/environment", + "mojo/public/cpp/system", + "mojo/public/cpp/utility", + ] +} diff --git a/mojo/public/cpp/environment/async_waiter.h b/mojo/public/cpp/environment/async_waiter.h new file mode 100644 index 0000000..83b7c8a --- /dev/null +++ b/mojo/public/cpp/environment/async_waiter.h @@ -0,0 +1,40 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_ENVIRONMENT_ASYNC_WAITER_H_ +#define MOJO_PUBLIC_CPP_ENVIRONMENT_ASYNC_WAITER_H_ + +#include "mojo/public/c/environment/async_waiter.h" +#include "mojo/public/cpp/bindings/callback.h" +#include "mojo/public/cpp/environment/environment.h" +#include "mojo/public/cpp/system/handle.h" + +namespace mojo { + +// A class that waits until a handle is ready and calls |callback| with the +// result. If the AsyncWaiter is deleted before the handle is ready, the wait is +// cancelled and the callback will not be called. +class AsyncWaiter { + public: + typedef mojo::Callback<void(MojoResult)> Callback; + + AsyncWaiter(Handle handle, + MojoHandleSignals signals, + const Callback& callback); + ~AsyncWaiter(); + + private: + static void WaitComplete(void* waiter, MojoResult result); + void WaitCompleteInternal(MojoResult result); + + const MojoAsyncWaiter* waiter_; + MojoAsyncWaitID id_; + const Callback callback_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(AsyncWaiter); +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_ENVIRONMENT_ASYNC_WAITER_H_ diff --git a/mojo/public/cpp/environment/environment.h b/mojo/public/cpp/environment/environment.h new file mode 100644 index 0000000..3a54428 --- /dev/null +++ b/mojo/public/cpp/environment/environment.h @@ -0,0 +1,53 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_ENVIRONMENT_ENVIRONMENT_H_ +#define MOJO_PUBLIC_CPP_ENVIRONMENT_ENVIRONMENT_H_ + +#include "mojo/public/cpp/system/macros.h" + +struct MojoAsyncWaiter; +struct MojoLogger; + +namespace mojo { + +struct TaskTracker; + +// Other parts of the Mojo C++ APIs use the *static* methods of this class. +// +// The "standalone" implementation of this class requires that this class (in +// the lib/ subdirectory) be instantiated (and remain so) while using the Mojo +// C++ APIs. I.e., the static methods depend on things set up by the constructor +// and torn down by the destructor. +// +// Other implementations may not have this requirement. +class Environment { + public: + Environment(); + // This constructor allows the standard implementations to be overridden (set + // a parameter to null to get the standard implementation). + Environment(const MojoAsyncWaiter* default_async_waiter, + const MojoLogger* default_logger, + const TaskTracker* default_task_tracker); + ~Environment(); + + static const MojoAsyncWaiter* GetDefaultAsyncWaiter(); + static const MojoLogger* GetDefaultLogger(); + static const TaskTracker* GetDefaultTaskTracker(); + + // These instantiate and destroy an environment-specific run loop for the + // current thread, allowing |GetDefaultAsyncWaiter()| to be used. (The run + // loop itself should be accessible via thread-local storage, using methods + // specific to the run loop implementation.) Creating and destroying nested + // run loops is not supported. + static void InstantiateDefaultRunLoop(); + static void DestroyDefaultRunLoop(); + + private: + MOJO_DISALLOW_COPY_AND_ASSIGN(Environment); +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_ENVIRONMENT_ENVIRONMENT_H_ diff --git a/mojo/public/cpp/environment/lib/async_waiter.cc b/mojo/public/cpp/environment/lib/async_waiter.cc new file mode 100644 index 0000000..599a649 --- /dev/null +++ b/mojo/public/cpp/environment/lib/async_waiter.cc @@ -0,0 +1,34 @@ +// 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. + +#include "mojo/public/cpp/environment/async_waiter.h" + +namespace mojo { + +AsyncWaiter::AsyncWaiter(Handle handle, + MojoHandleSignals signals, + const Callback& callback) + : waiter_(Environment::GetDefaultAsyncWaiter()), + id_(0), + callback_(callback) { + id_ = waiter_->AsyncWait(handle.value(), signals, MOJO_DEADLINE_INDEFINITE, + &AsyncWaiter::WaitComplete, this); +} + +AsyncWaiter::~AsyncWaiter() { + if (id_) + waiter_->CancelWait(id_); +} + +// static +void AsyncWaiter::WaitComplete(void* waiter, MojoResult result) { + static_cast<AsyncWaiter*>(waiter)->WaitCompleteInternal(result); +} + +void AsyncWaiter::WaitCompleteInternal(MojoResult result) { + id_ = 0; + callback_.Run(result); +} + +} // namespace mojo diff --git a/mojo/public/cpp/environment/lib/default_async_waiter.cc b/mojo/public/cpp/environment/lib/default_async_waiter.cc new file mode 100644 index 0000000..4a588a9 --- /dev/null +++ b/mojo/public/cpp/environment/lib/default_async_waiter.cc @@ -0,0 +1,85 @@ +// 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. + +#include "mojo/public/cpp/environment/lib/default_async_waiter.h" + +#include <assert.h> + +#include "mojo/public/c/environment/async_waiter.h" +#include "mojo/public/cpp/utility/run_loop.h" +#include "mojo/public/cpp/utility/run_loop_handler.h" + +namespace mojo { + +namespace { + +// RunLoopHandler implementation used for a request to AsyncWait(). There are +// two ways RunLoopHandlerImpl is deleted: +// . when the handle is ready (or errored). +// . when CancelWait() is invoked. +class RunLoopHandlerImpl : public RunLoopHandler { + public: + RunLoopHandlerImpl(const Handle& handle, + MojoAsyncWaitCallback callback, + void* closure) + : handle_(handle), callback_(callback), closure_(closure) {} + + ~RunLoopHandlerImpl() override { RunLoop::current()->RemoveHandler(handle_); } + + // RunLoopHandler: + void OnHandleReady(const Handle& handle) override { + NotifyCallback(MOJO_RESULT_OK); + } + + void OnHandleError(const Handle& handle, MojoResult result) override { + NotifyCallback(result); + } + + private: + void NotifyCallback(MojoResult result) { + // Delete this to unregister the handle. That way if the callback + // reregisters everything is ok. + MojoAsyncWaitCallback callback = callback_; + void* closure = closure_; + delete this; + + callback(closure, result); + } + + const Handle handle_; + MojoAsyncWaitCallback callback_; + void* closure_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(RunLoopHandlerImpl); +}; + +MojoAsyncWaitID AsyncWait(MojoHandle handle, + MojoHandleSignals signals, + MojoDeadline deadline, + MojoAsyncWaitCallback callback, + void* closure) { + RunLoop* run_loop = RunLoop::current(); + assert(run_loop); + + // |run_loop_handler| is destroyed either when the handle is ready or if + // CancelWait is invoked. + RunLoopHandlerImpl* run_loop_handler = + new RunLoopHandlerImpl(Handle(handle), callback, closure); + run_loop->AddHandler(run_loop_handler, Handle(handle), signals, deadline); + return reinterpret_cast<MojoAsyncWaitID>(run_loop_handler); +} + +void CancelWait(MojoAsyncWaitID wait_id) { + delete reinterpret_cast<RunLoopHandlerImpl*>(wait_id); +} + +} // namespace + +namespace internal { + +const MojoAsyncWaiter kDefaultAsyncWaiter = {AsyncWait, CancelWait}; + +} // namespace internal + +} // namespace mojo diff --git a/mojo/public/cpp/environment/lib/default_async_waiter.h b/mojo/public/cpp/environment/lib/default_async_waiter.h new file mode 100644 index 0000000..49ce233 --- /dev/null +++ b/mojo/public/cpp/environment/lib/default_async_waiter.h @@ -0,0 +1,18 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_ENVIRONMENT_LIB_DEFAULT_ASYNC_WAITER_H_ +#define MOJO_PUBLIC_CPP_ENVIRONMENT_LIB_DEFAULT_ASYNC_WAITER_H_ + +struct MojoAsyncWaiter; + +namespace mojo { +namespace internal { + +extern const MojoAsyncWaiter kDefaultAsyncWaiter; + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_ENVIRONMENT_LIB_DEFAULT_ASYNC_WAITER_H_ diff --git a/mojo/public/cpp/environment/lib/default_logger.cc b/mojo/public/cpp/environment/lib/default_logger.cc new file mode 100644 index 0000000..ba787d1 --- /dev/null +++ b/mojo/public/cpp/environment/lib/default_logger.cc @@ -0,0 +1,77 @@ +// 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. + +#include "mojo/public/cpp/environment/lib/default_logger.h" + +#include <stdio.h> +#include <stdlib.h> // For |abort()|. + +#include <algorithm> + +#include "mojo/public/c/environment/logger.h" + +namespace mojo { + +namespace { + +MojoLogLevel g_minimum_log_level = MOJO_LOG_LEVEL_INFO; + +const char* GetLogLevelString(MojoLogLevel log_level) { + if (log_level <= MOJO_LOG_LEVEL_VERBOSE - 3) + return "VERBOSE4+"; + switch (log_level) { + case MOJO_LOG_LEVEL_VERBOSE - 2: + return "VERBOSE3"; + case MOJO_LOG_LEVEL_VERBOSE - 1: + return "VERBOSE2"; + case MOJO_LOG_LEVEL_VERBOSE: + return "VERBOSE1"; + case MOJO_LOG_LEVEL_INFO: + return "INFO"; + case MOJO_LOG_LEVEL_WARNING: + return "WARNING"; + case MOJO_LOG_LEVEL_ERROR: + return "ERROR"; + } + // Consider everything higher to be fatal. + return "FATAL"; +} + +void LogMessage(MojoLogLevel log_level, + const char* source_file, + uint32_t source_line, + const char* message) { + if (log_level < g_minimum_log_level) + return; + + // TODO(vtl): Add timestamp also? + if (source_file) { + fprintf(stderr, "%s: %s(%u): %s\n", GetLogLevelString(log_level), + source_file, static_cast<unsigned>(source_line), message); + } else { + fprintf(stderr, "%s: %s\n", GetLogLevelString(log_level), message); + } + if (log_level >= MOJO_LOG_LEVEL_FATAL) + abort(); +} + +MojoLogLevel GetMinimumLogLevel() { + return g_minimum_log_level; +} + +void SetMinimumLogLevel(MojoLogLevel minimum_log_level) { + g_minimum_log_level = std::min(minimum_log_level, MOJO_LOG_LEVEL_FATAL); +} + +} // namespace + +namespace internal { + +const MojoLogger kDefaultLogger = {LogMessage, + GetMinimumLogLevel, + SetMinimumLogLevel}; + +} // namespace internal + +} // namespace mojo diff --git a/mojo/public/cpp/environment/lib/default_logger.h b/mojo/public/cpp/environment/lib/default_logger.h new file mode 100644 index 0000000..4db3233 --- /dev/null +++ b/mojo/public/cpp/environment/lib/default_logger.h @@ -0,0 +1,18 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_ENVIRONMENT_LIB_DEFAULT_LOGGER_H_ +#define MOJO_PUBLIC_CPP_ENVIRONMENT_LIB_DEFAULT_LOGGER_H_ + +struct MojoLogger; + +namespace mojo { +namespace internal { + +extern const MojoLogger kDefaultLogger; + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_ENVIRONMENT_LIB_DEFAULT_LOGGER_H_ diff --git a/mojo/public/cpp/environment/lib/default_task_tracker.cc b/mojo/public/cpp/environment/lib/default_task_tracker.cc new file mode 100644 index 0000000..a99f1c7 --- /dev/null +++ b/mojo/public/cpp/environment/lib/default_task_tracker.cc @@ -0,0 +1,40 @@ +// Copyright 2015 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. + +#include "mojo/public/cpp/environment/lib/default_task_tracker.h" + +#include "mojo/public/cpp/environment/task_tracker.h" + +namespace mojo { + +namespace { + +// +// The standalone task tracker does nothing. +// + +TaskTrackingId StartTracking(const char* function_name, + const char* file_name, + int line_number, + const void* program_counter) { + return TaskTrackingId(0); +} + +void EndTracking(const TaskTrackingId id) { +} + +void SetEnabled(bool enabled) { +} + +} // namespace + +namespace internal { + +const TaskTracker kDefaultTaskTracker = {&StartTracking, + &EndTracking, + &SetEnabled}; + +} // namespace internal + +} // namespace mojo diff --git a/mojo/public/cpp/environment/lib/default_task_tracker.h b/mojo/public/cpp/environment/lib/default_task_tracker.h new file mode 100644 index 0000000..7a0c064 --- /dev/null +++ b/mojo/public/cpp/environment/lib/default_task_tracker.h @@ -0,0 +1,19 @@ +// Copyright 2015 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_ENVIRONMENT_LIB_DEFAULT_TASK_TRACKER_H_ +#define MOJO_PUBLIC_CPP_ENVIRONMENT_LIB_DEFAULT_TASK_TRACKER_H_ + +namespace mojo { + +struct TaskTracker; + +namespace internal { + +extern const TaskTracker kDefaultTaskTracker; + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_ENVIRONMENT_LIB_DEFAULT_TASK_TRACKER_H_ diff --git a/mojo/public/cpp/environment/lib/environment.cc b/mojo/public/cpp/environment/lib/environment.cc new file mode 100644 index 0000000..8c7c930 --- /dev/null +++ b/mojo/public/cpp/environment/lib/environment.cc @@ -0,0 +1,93 @@ +// 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. + +#include "mojo/public/cpp/environment/environment.h" + +#include <assert.h> + +#include "mojo/public/c/environment/logger.h" +#include "mojo/public/cpp/environment/lib/default_async_waiter.h" +#include "mojo/public/cpp/environment/lib/default_logger.h" +#include "mojo/public/cpp/environment/lib/default_task_tracker.h" +#include "mojo/public/cpp/utility/run_loop.h" + +namespace mojo { + +namespace { + +const MojoAsyncWaiter* g_default_async_waiter = nullptr; +const MojoLogger* g_default_logger = nullptr; +const TaskTracker* g_default_task_tracker = nullptr; + +void Init(const MojoAsyncWaiter* default_async_waiter, + const MojoLogger* default_logger, + const TaskTracker* default_task_tracker) { + g_default_async_waiter = default_async_waiter + ? default_async_waiter + : &internal::kDefaultAsyncWaiter; + g_default_logger = + default_logger ? default_logger : &internal::kDefaultLogger; + + g_default_task_tracker = default_task_tracker + ? default_task_tracker + : &internal::kDefaultTaskTracker; + + RunLoop::SetUp(); +} + +} // namespace + +Environment::Environment() { + Init(nullptr, nullptr, nullptr); +} + +Environment::Environment(const MojoAsyncWaiter* default_async_waiter, + const MojoLogger* default_logger, + const TaskTracker* default_task_tracker) { + Init(default_async_waiter, default_logger, default_task_tracker); +} + +Environment::~Environment() { + RunLoop::TearDown(); + + // TODO(vtl): Maybe we should allow nesting, and restore previous default + // async waiters and loggers? + g_default_async_waiter = nullptr; + g_default_logger = nullptr; +} + +// static +const MojoAsyncWaiter* Environment::GetDefaultAsyncWaiter() { + assert(g_default_async_waiter); // Fails if not "inside" |Environment|. + return g_default_async_waiter; +} + +// static +const MojoLogger* Environment::GetDefaultLogger() { + assert(g_default_logger); // Fails if not "inside" |Environment|. + return g_default_logger; +} + +// static +const TaskTracker* Environment::GetDefaultTaskTracker() { + return g_default_task_tracker; +} + +// static +void Environment::InstantiateDefaultRunLoop() { + assert(!RunLoop::current()); + // Not leaked: accessible from |RunLoop::current()|. + RunLoop* run_loop = new RunLoop(); + MOJO_ALLOW_UNUSED_LOCAL(run_loop); + assert(run_loop == RunLoop::current()); +} + +// static +void Environment::DestroyDefaultRunLoop() { + assert(RunLoop::current()); + delete RunLoop::current(); + assert(!RunLoop::current()); +} + +} // namespace mojo diff --git a/mojo/public/cpp/environment/lib/logging.cc b/mojo/public/cpp/environment/lib/logging.cc new file mode 100644 index 0000000..57f1892 --- /dev/null +++ b/mojo/public/cpp/environment/lib/logging.cc @@ -0,0 +1,45 @@ +// 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. + +#include "mojo/public/cpp/environment/logging.h" + +#include "mojo/public/cpp/environment/environment.h" + +namespace mojo { +namespace internal { + +namespace { + +// Gets a pointer to the filename portion of |s|. Assumes that the filename +// follows the last slash or backslash in |s|, or is |s| if no slash or +// backslash is present. +// +// E.g., a pointer to "foo.cc" is returned for the following inputs: "foo.cc", +// "./foo.cc", ".\foo.cc", "/absolute/path/to/foo.cc", +// "relative/path/to/foo.cc", "C:\absolute\path\to\foo.cc", etc. +const char* GetFilename(const char* s) { + const char* rv = s; + while (*s) { + if (*s == '/' || *s == '\\') + rv = s + 1; + s++; + } + return rv; +} + +} // namespace + +// TODO(vtl): Maybe we should preserve the full path and strip it out at a +// different level instead? +LogMessage::LogMessage(MojoLogLevel log_level, const char* file, int line) + : log_level_(log_level), file_(GetFilename(file)), line_(line) { +} + +LogMessage::~LogMessage() { + Environment::GetDefaultLogger()->LogMessage( + log_level_, file_, static_cast<uint32_t>(line_), stream_.str().c_str()); +} + +} // namespace internal +} // namespace mojo diff --git a/mojo/public/cpp/environment/lib/scoped_task_tracking.cc b/mojo/public/cpp/environment/lib/scoped_task_tracking.cc new file mode 100644 index 0000000..9e253d5 --- /dev/null +++ b/mojo/public/cpp/environment/lib/scoped_task_tracking.cc @@ -0,0 +1,37 @@ +// Copyright 2015 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. + +#include "mojo/public/cpp/environment/lib/scoped_task_tracking.h" + +#include "mojo/public/cpp/environment/environment.h" + +namespace mojo { +namespace internal { + +ScopedTaskTracking::ScopedTaskTracking(const char* function_name, + const char* file_name, + int line, + const void* program_counter) + : id_(Environment::GetDefaultTaskTracker()->StartTracking( + function_name, + file_name, + line, + program_counter)) { +} + +ScopedTaskTracking::ScopedTaskTracking(const char* function_name, + const char* file_name, + int line) + : id_(Environment::GetDefaultTaskTracker()->StartTracking(function_name, + file_name, + line, + nullptr)) { +} + +ScopedTaskTracking::~ScopedTaskTracking() { + Environment::GetDefaultTaskTracker()->EndTracking(id_); +} + +} // namespace internal +} // namespace mojo diff --git a/mojo/public/cpp/environment/lib/scoped_task_tracking.h b/mojo/public/cpp/environment/lib/scoped_task_tracking.h new file mode 100644 index 0000000..b6e32c1 --- /dev/null +++ b/mojo/public/cpp/environment/lib/scoped_task_tracking.h @@ -0,0 +1,34 @@ +// Copyright 2015 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_ENVIRONMENT_LIB_SCOPED_TASK_TRACKING_H_ +#define MOJO_PUBLIC_CPP_ENVIRONMENT_LIB_SCOPED_TASK_TRACKING_H_ + +#include "mojo/public/cpp/environment/task_tracker.h" +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { +namespace internal { + +// An RAII wrapper for |TaskTrackingId|. +class ScopedTaskTracking { + public: + ScopedTaskTracking(const char* function_name, + const char* file_name, + int line, + const void* program_counter); + ScopedTaskTracking(const char* function_name, + const char* file_name, + int line); + ~ScopedTaskTracking(); + + private: + TaskTrackingId id_; + MOJO_DISALLOW_COPY_AND_ASSIGN(ScopedTaskTracking); +}; + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_ENVIRONMENT_LIB_SCOPED_TASK_TRACKING_H_ diff --git a/mojo/public/cpp/environment/logging.h b/mojo/public/cpp/environment/logging.h new file mode 100644 index 0000000..bfc5c13 --- /dev/null +++ b/mojo/public/cpp/environment/logging.h @@ -0,0 +1,90 @@ +// 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. + +// Logging macros, similar to Chromium's base/logging.h, except with |MOJO_| +// prefixes and missing some features (notably |CHECK_EQ()|, etc.). + +// TODO(vtl): It's weird that this is in the environment directory, since its +// implementation (in environment/lib) is meant to be used by any implementation +// of the environment. + +#ifndef MOJO_PUBLIC_CPP_ENVIRONMENT_LOGGING_H_ +#define MOJO_PUBLIC_CPP_ENVIRONMENT_LOGGING_H_ + +#include <sstream> + +#include "mojo/public/c/environment/logger.h" +#include "mojo/public/cpp/environment/environment.h" +#include "mojo/public/cpp/system/macros.h" + +#define MOJO_LOG_STREAM(level) \ + ::mojo::internal::LogMessage(MOJO_LOG_LEVEL_##level, __FILE__, __LINE__) \ + .stream() + +#define MOJO_LAZY_LOG_STREAM(level, condition) \ + !(condition) ? (void)0 \ + : ::mojo::internal::VoidifyOstream() & MOJO_LOG_STREAM(level) + +#define MOJO_SHOULD_LOG(level) \ + (MOJO_LOG_LEVEL_##level >= \ + ::mojo::Environment::GetDefaultLogger()->GetMinimumLogLevel()) + +#define MOJO_LOG(level) MOJO_LAZY_LOG_STREAM(level, MOJO_SHOULD_LOG(level)) + +#define MOJO_LOG_IF(level, condition) \ + MOJO_LAZY_LOG_STREAM(level, MOJO_SHOULD_LOG(level) && (condition)) + +#define MOJO_CHECK(condition) \ + MOJO_LAZY_LOG_STREAM(FATAL, !(condition)) << "Check failed: " #condition "." \ + " " + +// Note: For non-debug builds, |MOJO_DLOG_IF()| *eliminates* (i.e., doesn't +// compile) the condition, whereas |MOJO_DCHECK()| "neuters" the condition +// (i.e., compiles, but doesn't evaluate). +#ifdef NDEBUG +#define MOJO_DLOG(level) MOJO_LAZY_LOG_STREAM(level, false) +#define MOJO_DLOG_IF(level, condition) MOJO_LAZY_LOG_STREAM(level, false) +#else +#define MOJO_DLOG(level) MOJO_LOG(level) +#define MOJO_DLOG_IF(level, condition) MOJO_LOG_IF(level, condition) +#endif // NDEBUG + +#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON) +#define MOJO_DCHECK(condition) \ + MOJO_LAZY_LOG_STREAM(FATAL, false ? !(condition) : false) +#else +#define MOJO_DCHECK(condition) MOJO_CHECK(condition) +#endif // NDEBUG && !defined(DCHECK_ALWAYS_ON) + +#define MOJO_NOTREACHED() MOJO_DCHECK(false) + +namespace mojo { +namespace internal { + +class LogMessage { + public: + LogMessage(MojoLogLevel log_level, const char* file, int line); + ~LogMessage(); + + std::ostream& stream() { return stream_; } + + private: + const MojoLogLevel log_level_; + const char* const file_; + const int line_; + std::ostringstream stream_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(LogMessage); +}; + +// Used to ignore a stream. +struct VoidifyOstream { + // Use & since it has precedence lower than << but higher than ?:. + void operator&(std::ostream&) {} +}; + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_ENVIRONMENT_LOGGING_H_ diff --git a/mojo/public/cpp/environment/task_tracker.h b/mojo/public/cpp/environment/task_tracker.h new file mode 100644 index 0000000..d6d3020 --- /dev/null +++ b/mojo/public/cpp/environment/task_tracker.h @@ -0,0 +1,30 @@ +// Copyright 2015 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_ENVIRONMENT_TASK_TRACKER_H_ +#define MOJO_PUBLIC_CPP_ENVIRONMENT_TASK_TRACKER_H_ + +#include <stdint.h> + +namespace mojo { + +typedef intptr_t TaskTrackingId; + +// Interface for wiring task-level profiling. This API is mainly used by the +// generated interface implementation. +struct TaskTracker { + // Start tracking. The returned id must be reclaimed through |EndTracking()|. + TaskTrackingId (*StartTracking)(const char* function_name, + const char* file_name, + int line_number, + const void* program_counter); + // Finish tracking. The |id| is one that is returned from |StartTracking()|. + void (*EndTracking)(const TaskTrackingId id); + // Enable or disable tracking. It is disabled by default. + void (*SetEnabled)(bool enabled); +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_ENVIRONMENT_TASK_TRACKER_H_ diff --git a/mojo/public/cpp/environment/tests/BUILD.gn b/mojo/public/cpp/environment/tests/BUILD.gn new file mode 100644 index 0000000..10fd056 --- /dev/null +++ b/mojo/public/cpp/environment/tests/BUILD.gn @@ -0,0 +1,29 @@ +# 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. + +import("../../../mojo_sdk.gni") + +mojo_sdk_source_set("tests") { + testonly = true + + sources = [ + "async_wait_unittest.cc", + "async_waiter_unittest.cc", + "logger_unittest.cc", + "logging_unittest.cc", + ] + + deps = [ + "//testing/gtest", + ] + + mojo_sdk_deps = [ + "mojo/public/c/environment", + "mojo/public/cpp/bindings:callback", + "mojo/public/cpp/environment:standalone", + "mojo/public/cpp/system", + "mojo/public/cpp/test_support:test_utils", + "mojo/public/cpp/utility", + ] +} diff --git a/mojo/public/cpp/environment/tests/async_wait_unittest.cc b/mojo/public/cpp/environment/tests/async_wait_unittest.cc new file mode 100644 index 0000000..83c5ca0 --- /dev/null +++ b/mojo/public/cpp/environment/tests/async_wait_unittest.cc @@ -0,0 +1,114 @@ +// 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. + +#include <string> + +#include "mojo/public/c/environment/async_waiter.h" +#include "mojo/public/cpp/environment/environment.h" +#include "mojo/public/cpp/system/core.h" +#include "mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/test_support/test_utils.h" +#include "mojo/public/cpp/utility/run_loop.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace { + +class TestAsyncWaitCallback { + public: + TestAsyncWaitCallback() : result_count_(0), last_result_(MOJO_RESULT_OK) {} + ~TestAsyncWaitCallback() {} + + int result_count() const { return result_count_; } + + MojoResult last_result() const { return last_result_; } + + // MojoAsyncWaitCallback: + static void OnHandleReady(void* closure, MojoResult result) { + TestAsyncWaitCallback* self = static_cast<TestAsyncWaitCallback*>(closure); + self->result_count_++; + self->last_result_ = result; + } + + private: + int result_count_; + MojoResult last_result_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(TestAsyncWaitCallback); +}; + +MojoAsyncWaitID CallAsyncWait(const Handle& handle, + MojoHandleSignals signals, + TestAsyncWaitCallback* callback) { + return Environment::GetDefaultAsyncWaiter()->AsyncWait( + handle.value(), + signals, + MOJO_DEADLINE_INDEFINITE, + &TestAsyncWaitCallback::OnHandleReady, + callback); +} + +void CallCancelWait(MojoAsyncWaitID wait_id) { + Environment::GetDefaultAsyncWaiter()->CancelWait(wait_id); +} + +class AsyncWaitTest : public testing::Test { + public: + AsyncWaitTest() {} + + private: + Environment environment_; + RunLoop run_loop_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(AsyncWaitTest); +}; + +// Verifies AsyncWaitCallback is notified when pipe is ready. +TEST_F(AsyncWaitTest, CallbackNotified) { + TestAsyncWaitCallback callback; + MessagePipe test_pipe; + EXPECT_TRUE(test::WriteTextMessage(test_pipe.handle1.get(), std::string())); + + CallAsyncWait( + test_pipe.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE, &callback); + RunLoop::current()->Run(); + EXPECT_EQ(1, callback.result_count()); + EXPECT_EQ(MOJO_RESULT_OK, callback.last_result()); +} + +// Verifies 2 AsyncWaitCallbacks are notified when there pipes are ready. +TEST_F(AsyncWaitTest, TwoCallbacksNotified) { + TestAsyncWaitCallback callback1; + TestAsyncWaitCallback callback2; + MessagePipe test_pipe1; + MessagePipe test_pipe2; + EXPECT_TRUE(test::WriteTextMessage(test_pipe1.handle1.get(), std::string())); + EXPECT_TRUE(test::WriteTextMessage(test_pipe2.handle1.get(), std::string())); + + CallAsyncWait( + test_pipe1.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE, &callback1); + CallAsyncWait( + test_pipe2.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE, &callback2); + + RunLoop::current()->Run(); + EXPECT_EQ(1, callback1.result_count()); + EXPECT_EQ(MOJO_RESULT_OK, callback1.last_result()); + EXPECT_EQ(1, callback2.result_count()); + EXPECT_EQ(MOJO_RESULT_OK, callback2.last_result()); +} + +// Verifies cancel works. +TEST_F(AsyncWaitTest, CancelCallback) { + TestAsyncWaitCallback callback; + MessagePipe test_pipe; + EXPECT_TRUE(test::WriteTextMessage(test_pipe.handle1.get(), std::string())); + + CallCancelWait(CallAsyncWait( + test_pipe.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE, &callback)); + RunLoop::current()->Run(); + EXPECT_EQ(0, callback.result_count()); +} + +} // namespace +} // namespace mojo diff --git a/mojo/public/cpp/environment/tests/async_waiter_unittest.cc b/mojo/public/cpp/environment/tests/async_waiter_unittest.cc new file mode 100644 index 0000000..1c1c2bf --- /dev/null +++ b/mojo/public/cpp/environment/tests/async_waiter_unittest.cc @@ -0,0 +1,107 @@ +// 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. + +#include "mojo/public/cpp/bindings/callback.h" +#include "mojo/public/cpp/environment/async_waiter.h" +#include "mojo/public/cpp/test_support/test_utils.h" +#include "mojo/public/cpp/utility/run_loop.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace { + +class TestAsyncWaitCallback { + public: + TestAsyncWaitCallback() : result_count_(0), last_result_(MOJO_RESULT_OK) {} + ~TestAsyncWaitCallback() {} + + int result_count() const { return result_count_; } + + MojoResult last_result() const { return last_result_; } + + void OnHandleReady(MojoResult result) { + result_count_++; + last_result_ = result; + } + + private: + int result_count_; + MojoResult last_result_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(TestAsyncWaitCallback); +}; + +// Manual code to create a callback since we don't have mojo::Bind yet. +class ManualCallback { + public: + explicit ManualCallback(TestAsyncWaitCallback* callback) + : callback_(callback) {} + + void Run(MojoResult result) const { callback_->OnHandleReady(result); } + + private: + TestAsyncWaitCallback* callback_; +}; + +class AsyncWaiterTest : public testing::Test { + public: + AsyncWaiterTest() {} + + private: + Environment environment_; + RunLoop run_loop_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(AsyncWaiterTest); +}; + +// Verifies AsyncWaitCallback is notified when pipe is ready. +TEST_F(AsyncWaiterTest, CallbackNotified) { + TestAsyncWaitCallback callback; + MessagePipe test_pipe; + EXPECT_TRUE(test::WriteTextMessage(test_pipe.handle1.get(), std::string())); + + AsyncWaiter waiter(test_pipe.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE, + ManualCallback(&callback)); + RunLoop::current()->Run(); + EXPECT_EQ(1, callback.result_count()); + EXPECT_EQ(MOJO_RESULT_OK, callback.last_result()); +} + +// Verifies 2 AsyncWaitCallbacks are notified when there pipes are ready. +TEST_F(AsyncWaiterTest, TwoCallbacksNotified) { + TestAsyncWaitCallback callback1; + TestAsyncWaitCallback callback2; + MessagePipe test_pipe1; + MessagePipe test_pipe2; + EXPECT_TRUE(test::WriteTextMessage(test_pipe1.handle1.get(), std::string())); + EXPECT_TRUE(test::WriteTextMessage(test_pipe2.handle1.get(), std::string())); + + AsyncWaiter waiter1(test_pipe1.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE, + ManualCallback(&callback1)); + AsyncWaiter waiter2(test_pipe2.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE, + ManualCallback(&callback2)); + + RunLoop::current()->Run(); + EXPECT_EQ(1, callback1.result_count()); + EXPECT_EQ(MOJO_RESULT_OK, callback1.last_result()); + EXPECT_EQ(1, callback2.result_count()); + EXPECT_EQ(MOJO_RESULT_OK, callback2.last_result()); +} + +// Verifies cancel works. +TEST_F(AsyncWaiterTest, CancelCallback) { + TestAsyncWaitCallback callback; + MessagePipe test_pipe; + EXPECT_TRUE(test::WriteTextMessage(test_pipe.handle1.get(), std::string())); + + { + AsyncWaiter waiter(test_pipe.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE, + ManualCallback(&callback)); + } + RunLoop::current()->Run(); + EXPECT_EQ(0, callback.result_count()); +} + +} // namespace +} // namespace mojo diff --git a/mojo/public/cpp/environment/tests/logger_unittest.cc b/mojo/public/cpp/environment/tests/logger_unittest.cc new file mode 100644 index 0000000..8ca5538 --- /dev/null +++ b/mojo/public/cpp/environment/tests/logger_unittest.cc @@ -0,0 +1,89 @@ +// 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. + +#include "mojo/public/c/environment/logger.h" +#include "mojo/public/cpp/environment/environment.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace { + +TEST(LoggerTest, Basic) { + const char kPath[] = "/fake/path/to/file.cc"; + + Environment environment; + const MojoLogger* const logger = Environment::GetDefaultLogger(); + + logger->LogMessage(MOJO_LOG_LEVEL_VERBOSE - 1, kPath, 123, + "Logged at VERBOSE-1 level"); + logger->LogMessage(MOJO_LOG_LEVEL_VERBOSE, kPath, 123, + "Logged at VERBOSE level"); + logger->LogMessage(MOJO_LOG_LEVEL_INFO, kPath, 123, "Logged at INFO level"); + logger->LogMessage(MOJO_LOG_LEVEL_WARNING, kPath, 123, + "Logged at WARNING level"); + logger->LogMessage(MOJO_LOG_LEVEL_ERROR, kPath, 123, "Logged at ERROR level"); + + // This should kill us: + EXPECT_DEATH_IF_SUPPORTED({ + logger->LogMessage(MOJO_LOG_LEVEL_FATAL, kPath, 123, + "Logged at FATAL level"); + }, ""); +} + +TEST(LoggerTest, LogLevels) { + const char kPath[] = "/fake/path/to/file.cc"; + + Environment environment; + const MojoLogger* const logger = Environment::GetDefaultLogger(); + + for (MojoLogLevel log_level = MOJO_LOG_LEVEL_VERBOSE - 1; + log_level <= MOJO_LOG_LEVEL_FATAL + 1; + log_level++) { + logger->SetMinimumLogLevel(log_level); + + if (log_level <= MOJO_LOG_LEVEL_FATAL) + EXPECT_EQ(log_level, logger->GetMinimumLogLevel()); + else + EXPECT_EQ(MOJO_LOG_LEVEL_FATAL, logger->GetMinimumLogLevel()); + + logger->LogMessage(MOJO_LOG_LEVEL_VERBOSE - 1, kPath, 123, + "Logged at VERBOSE-1 level"); + logger->LogMessage(MOJO_LOG_LEVEL_VERBOSE, kPath, 123, + "Logged at VERBOSE level"); + logger->LogMessage(MOJO_LOG_LEVEL_INFO, kPath, 123, "Logged at INFO level"); + logger->LogMessage(MOJO_LOG_LEVEL_WARNING, kPath, 123, + "Logged at WARNING level"); + logger->LogMessage(MOJO_LOG_LEVEL_ERROR, kPath, 123, + "Logged at ERROR level"); + + // This should kill us: + EXPECT_DEATH_IF_SUPPORTED({ + logger->LogMessage(MOJO_LOG_LEVEL_FATAL, kPath, 123, + "Logged at FATAL level"); + }, ""); + } +} + +TEST(LoggerTest, NoFile) { + Environment environment; + const MojoLogger* const logger = Environment::GetDefaultLogger(); + + logger->LogMessage(MOJO_LOG_LEVEL_VERBOSE - 1, nullptr, 0, + "Logged at VERBOSE-1 level"); + logger->LogMessage(MOJO_LOG_LEVEL_VERBOSE, nullptr, 0, + "Logged at VERBOSE level"); + logger->LogMessage(MOJO_LOG_LEVEL_INFO, nullptr, 0, "Logged at INFO level"); + logger->LogMessage(MOJO_LOG_LEVEL_WARNING, nullptr, 0, + "Logged at WARNING level"); + logger->LogMessage(MOJO_LOG_LEVEL_ERROR, nullptr, 0, "Logged at ERROR level"); + + // This should kill us: + EXPECT_DEATH_IF_SUPPORTED({ + logger->LogMessage(MOJO_LOG_LEVEL_FATAL, nullptr, 0, + "Logged at FATAL level"); + }, ""); +} + +} // namespace +} // namespace mojo diff --git a/mojo/public/cpp/environment/tests/logging_unittest.cc b/mojo/public/cpp/environment/tests/logging_unittest.cc new file mode 100644 index 0000000..489888c --- /dev/null +++ b/mojo/public/cpp/environment/tests/logging_unittest.cc @@ -0,0 +1,521 @@ +// 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. + +#include <stdlib.h> + +#include <sstream> +#include <string> + +#include "mojo/public/cpp/environment/environment.h" +#include "mojo/public/cpp/environment/logging.h" +#include "mojo/public/cpp/system/macros.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace { + +// The current logging system strips the path, so we need our filename. +const char kOurFilename[] = "logging_unittest.cc"; + +class PtrToMemberHelper { + public: + int member; +}; + +bool DcheckTestHelper(bool* was_called) { + *was_called = true; + return false; +} + +class LoggingTest : public testing::Test { + public: + LoggingTest() : environment_(nullptr, &kMockLogger, nullptr) { + minimum_log_level_ = MOJO_LOG_LEVEL_INFO; + ResetMockLogger(); + } + ~LoggingTest() override {} + + protected: + // Note: Does not reset |minimum_log_level_|. + static void ResetMockLogger() { + log_message_was_called_ = false; + last_log_level_ = MOJO_LOG_LEVEL_INFO; + last_source_file_.clear(); + last_source_line_ = 0; + last_message_.clear(); + } + + // A function returning |bool| that shouldn't be called. + static bool NotCalledCondition() { + not_called_condition_was_called_ = true; + return false; + } + + static bool log_message_was_called() { return log_message_was_called_; } + static MojoLogLevel last_log_level() { return last_log_level_; } + static const std::string& last_source_file() { return last_source_file_; } + static uint32_t last_source_line() { return last_source_line_; } + static const std::string& last_message() { return last_message_; } + static bool not_called_condition_was_called() { + return not_called_condition_was_called_; + } + + private: + // Note: We record calls even if |log_level| is below |minimum_log_level_| + // (since the macros should mostly avoid this, and we want to be able to check + // that they do). + static void MockLogMessage(MojoLogLevel log_level, + const char* source_file, + uint32_t source_line, + const char* message) { + log_message_was_called_ = true; + last_log_level_ = log_level; + last_source_file_ = source_file; + last_source_line_ = source_line; + last_message_ = message; + } + + static MojoLogLevel MockGetMinimumLogLevel() { return minimum_log_level_; } + + static void MockSetMinimumLogLevel(MojoLogLevel minimum_log_level) { + minimum_log_level_ = minimum_log_level; + } + + Environment environment_; + + static const MojoLogger kMockLogger; + static MojoLogLevel minimum_log_level_; + static bool log_message_was_called_; + static MojoLogLevel last_log_level_; + static std::string last_source_file_; + static uint32_t last_source_line_; + static std::string last_message_; + static bool not_called_condition_was_called_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(LoggingTest); +}; + +// static +const MojoLogger LoggingTest::kMockLogger = { + &LoggingTest::MockLogMessage, + &LoggingTest::MockGetMinimumLogLevel, + &LoggingTest::MockSetMinimumLogLevel}; + +// static +MojoLogLevel LoggingTest::minimum_log_level_ = MOJO_LOG_LEVEL_INFO; + +// static +bool LoggingTest::log_message_was_called_ = MOJO_LOG_LEVEL_INFO; + +// static +MojoLogLevel LoggingTest::last_log_level_ = MOJO_LOG_LEVEL_INFO; + +// static +std::string LoggingTest::last_source_file_; + +// static +uint32_t LoggingTest::last_source_line_ = 0; + +// static +std::string LoggingTest::last_message_; + +// static +bool LoggingTest::not_called_condition_was_called_ = false; + +TEST_F(LoggingTest, InternalLogMessage) { + internal::LogMessage(MOJO_LOG_LEVEL_INFO, "foo.cc", 123).stream() << "hello " + << "world"; + EXPECT_TRUE(log_message_was_called()); + EXPECT_EQ(MOJO_LOG_LEVEL_INFO, last_log_level()); + EXPECT_EQ("foo.cc", last_source_file()); + EXPECT_EQ(123u, last_source_line()); + EXPECT_EQ("hello world", last_message()); + + ResetMockLogger(); + + internal::LogMessage(MOJO_LOG_LEVEL_WARNING, "./path/to/foo.cc", 123).stream() + << "hello " + << "world"; + EXPECT_TRUE(log_message_was_called()); + EXPECT_EQ(MOJO_LOG_LEVEL_WARNING, last_log_level()); + EXPECT_EQ("foo.cc", last_source_file()); + EXPECT_EQ(123u, last_source_line()); + EXPECT_EQ("hello world", last_message()); + + ResetMockLogger(); + + internal::LogMessage(MOJO_LOG_LEVEL_ERROR, "/path/to/foo.cc", 123).stream() + << "hello " + << "world"; + EXPECT_TRUE(log_message_was_called()); + EXPECT_EQ(MOJO_LOG_LEVEL_ERROR, last_log_level()); + EXPECT_EQ("foo.cc", last_source_file()); + EXPECT_EQ(123u, last_source_line()); + EXPECT_EQ("hello world", last_message()); + + ResetMockLogger(); + + internal::LogMessage(MOJO_LOG_LEVEL_FATAL, "path/to/foo.cc", 123).stream() + << "hello " + << "world"; + EXPECT_TRUE(log_message_was_called()); + EXPECT_EQ(MOJO_LOG_LEVEL_FATAL, last_log_level()); + EXPECT_EQ("foo.cc", last_source_file()); + EXPECT_EQ(123u, last_source_line()); + EXPECT_EQ("hello world", last_message()); + + ResetMockLogger(); + + internal::LogMessage(MOJO_LOG_LEVEL_VERBOSE, ".\\xy\\foo.cc", 123).stream() + << "hello " + << "world"; + EXPECT_TRUE(log_message_was_called()); + EXPECT_EQ(MOJO_LOG_LEVEL_VERBOSE, last_log_level()); + EXPECT_EQ("foo.cc", last_source_file()); + EXPECT_EQ(123u, last_source_line()); + EXPECT_EQ("hello world", last_message()); + + ResetMockLogger(); + + internal::LogMessage(MOJO_LOG_LEVEL_VERBOSE - 1, "xy\\foo.cc", 123).stream() + << "hello " + << "world"; + EXPECT_TRUE(log_message_was_called()); + EXPECT_EQ(MOJO_LOG_LEVEL_VERBOSE - 1, last_log_level()); + EXPECT_EQ("foo.cc", last_source_file()); + EXPECT_EQ(123u, last_source_line()); + EXPECT_EQ("hello world", last_message()); + + ResetMockLogger(); + + internal::LogMessage(MOJO_LOG_LEVEL_VERBOSE - 9, "C:\\xy\\foo.cc", 123) + .stream() + << "hello " + << "world"; + EXPECT_TRUE(log_message_was_called()); + EXPECT_EQ(MOJO_LOG_LEVEL_VERBOSE - 9, last_log_level()); + EXPECT_EQ("foo.cc", last_source_file()); + EXPECT_EQ(123u, last_source_line()); + EXPECT_EQ("hello world", last_message()); + + ResetMockLogger(); + + internal::LogMessage(MOJO_LOG_LEVEL_INFO, __FILE__, 123).stream() << "hello " + << "world"; + EXPECT_TRUE(log_message_was_called()); + EXPECT_EQ(MOJO_LOG_LEVEL_INFO, last_log_level()); + EXPECT_EQ(kOurFilename, last_source_file()); + EXPECT_EQ(123u, last_source_line()); + EXPECT_EQ("hello world", last_message()); +} + +TEST_F(LoggingTest, LogStream) { + MOJO_LOG_STREAM(INFO) << "hello"; + EXPECT_TRUE(log_message_was_called()); + EXPECT_EQ(MOJO_LOG_LEVEL_INFO, last_log_level()); + EXPECT_EQ(kOurFilename, last_source_file()); + EXPECT_EQ(static_cast<uint32_t>(__LINE__ - 4), last_source_line()); + EXPECT_EQ("hello", last_message()); + + ResetMockLogger(); + + MOJO_LOG_STREAM(ERROR) << "hi " << 123; + EXPECT_TRUE(log_message_was_called()); + EXPECT_EQ(MOJO_LOG_LEVEL_ERROR, last_log_level()); + EXPECT_EQ(kOurFilename, last_source_file()); + EXPECT_EQ(static_cast<uint32_t>(__LINE__ - 4), last_source_line()); + EXPECT_EQ("hi 123", last_message()); +} + +TEST_F(LoggingTest, LazyLogStream) { + MOJO_LAZY_LOG_STREAM(INFO, true) << "hello"; + EXPECT_TRUE(log_message_was_called()); + EXPECT_EQ(MOJO_LOG_LEVEL_INFO, last_log_level()); + EXPECT_EQ(kOurFilename, last_source_file()); + EXPECT_EQ(static_cast<uint32_t>(__LINE__ - 4), last_source_line()); + EXPECT_EQ("hello", last_message()); + + ResetMockLogger(); + + MOJO_LAZY_LOG_STREAM(ERROR, true) << "hi " << 123; + EXPECT_TRUE(log_message_was_called()); + EXPECT_EQ(MOJO_LOG_LEVEL_ERROR, last_log_level()); + EXPECT_EQ(kOurFilename, last_source_file()); + EXPECT_EQ(static_cast<uint32_t>(__LINE__ - 4), last_source_line()); + EXPECT_EQ("hi 123", last_message()); + + ResetMockLogger(); + + MOJO_LAZY_LOG_STREAM(INFO, false) << "hello"; + EXPECT_FALSE(log_message_was_called()); + + ResetMockLogger(); + + MOJO_LAZY_LOG_STREAM(FATAL, false) << "hello"; + EXPECT_FALSE(log_message_was_called()); + + ResetMockLogger(); + + PtrToMemberHelper helper; + helper.member = 1; + int PtrToMemberHelper::*member_ptr = &PtrToMemberHelper::member; + + // This probably fails to compile if we forget to parenthesize the condition + // in the macro (.* has lower precedence than !, which can't apply to + // |helper|). + MOJO_LAZY_LOG_STREAM(ERROR, helper.*member_ptr == 1) << "hello"; + EXPECT_TRUE(log_message_was_called()); + + ResetMockLogger(); + + MOJO_LAZY_LOG_STREAM(WARNING, helper.*member_ptr == 0) << "hello"; + EXPECT_FALSE(log_message_was_called()); +} + +TEST_F(LoggingTest, ShouldLog) { + // We start at |MOJO_LOG_LEVEL_INFO|. + EXPECT_FALSE(MOJO_SHOULD_LOG(VERBOSE)); + EXPECT_TRUE(MOJO_SHOULD_LOG(INFO)); + EXPECT_TRUE(MOJO_SHOULD_LOG(WARNING)); + EXPECT_TRUE(MOJO_SHOULD_LOG(ERROR)); + EXPECT_TRUE(MOJO_SHOULD_LOG(FATAL)); + + Environment::GetDefaultLogger()->SetMinimumLogLevel(MOJO_LOG_LEVEL_ERROR); + EXPECT_FALSE(MOJO_SHOULD_LOG(VERBOSE)); + EXPECT_FALSE(MOJO_SHOULD_LOG(INFO)); + EXPECT_FALSE(MOJO_SHOULD_LOG(WARNING)); + EXPECT_TRUE(MOJO_SHOULD_LOG(ERROR)); + EXPECT_TRUE(MOJO_SHOULD_LOG(FATAL)); + + Environment::GetDefaultLogger()->SetMinimumLogLevel(MOJO_LOG_LEVEL_VERBOSE - + 1); + EXPECT_TRUE(MOJO_SHOULD_LOG(VERBOSE)); + EXPECT_TRUE(MOJO_SHOULD_LOG(INFO)); + EXPECT_TRUE(MOJO_SHOULD_LOG(WARNING)); + EXPECT_TRUE(MOJO_SHOULD_LOG(ERROR)); + EXPECT_TRUE(MOJO_SHOULD_LOG(FATAL)); +} + +TEST_F(LoggingTest, Log) { + // We start at |MOJO_LOG_LEVEL_INFO|. + MOJO_LOG(VERBOSE) << "hello"; + EXPECT_FALSE(log_message_was_called()); + + ResetMockLogger(); + + MOJO_LOG(INFO) << "hello"; + EXPECT_TRUE(log_message_was_called()); + EXPECT_EQ(MOJO_LOG_LEVEL_INFO, last_log_level()); + EXPECT_EQ(kOurFilename, last_source_file()); + EXPECT_EQ(static_cast<uint32_t>(__LINE__ - 4), last_source_line()); + EXPECT_EQ("hello", last_message()); + + ResetMockLogger(); + + MOJO_LOG(ERROR) << "hello"; + EXPECT_TRUE(log_message_was_called()); + EXPECT_EQ(MOJO_LOG_LEVEL_ERROR, last_log_level()); + EXPECT_EQ(kOurFilename, last_source_file()); + EXPECT_EQ(static_cast<uint32_t>(__LINE__ - 4), last_source_line()); + EXPECT_EQ("hello", last_message()); + + ResetMockLogger(); + + Environment::GetDefaultLogger()->SetMinimumLogLevel(MOJO_LOG_LEVEL_ERROR); + + MOJO_LOG(VERBOSE) << "hello"; + EXPECT_FALSE(log_message_was_called()); + + ResetMockLogger(); + + MOJO_LOG(INFO) << "hello"; + EXPECT_FALSE(log_message_was_called()); + + ResetMockLogger(); + + MOJO_LOG(ERROR) << "hello"; + EXPECT_TRUE(log_message_was_called()); + EXPECT_EQ(MOJO_LOG_LEVEL_ERROR, last_log_level()); + EXPECT_EQ(kOurFilename, last_source_file()); + EXPECT_EQ(static_cast<uint32_t>(__LINE__ - 4), last_source_line()); + EXPECT_EQ("hello", last_message()); +} + +TEST_F(LoggingTest, LogIf) { + // We start at |MOJO_LOG_LEVEL_INFO|. + MOJO_LOG_IF(VERBOSE, true) << "hello"; + EXPECT_FALSE(log_message_was_called()); + + ResetMockLogger(); + + MOJO_LOG_IF(VERBOSE, false) << "hello"; + EXPECT_FALSE(log_message_was_called()); + + ResetMockLogger(); + Environment::GetDefaultLogger()->SetMinimumLogLevel(MOJO_LOG_LEVEL_ERROR); + + bool x = true; + // Also try to make sure that we parenthesize the condition properly. + MOJO_LOG_IF(INFO, false || x) << "hello"; + EXPECT_FALSE(log_message_was_called()); + + ResetMockLogger(); + + MOJO_LOG_IF(INFO, 0 != 1) << "hello"; + EXPECT_FALSE(log_message_was_called()); + + ResetMockLogger(); + + MOJO_LOG_IF(WARNING, 1 + 1 == 2) << "hello"; + EXPECT_FALSE(log_message_was_called()); + + ResetMockLogger(); + + MOJO_LOG_IF(ERROR, 1 * 2 == 2) << "hello"; + EXPECT_TRUE(log_message_was_called()); + EXPECT_EQ(MOJO_LOG_LEVEL_ERROR, last_log_level()); + EXPECT_EQ(kOurFilename, last_source_file()); + EXPECT_EQ(static_cast<uint32_t>(__LINE__ - 4), last_source_line()); + EXPECT_EQ("hello", last_message()); + + ResetMockLogger(); + + MOJO_LOG_IF(FATAL, 1 * 2 == 3) << "hello"; + EXPECT_FALSE(log_message_was_called()); + + ResetMockLogger(); + + // |MOJO_LOG_IF()| shouldn't evaluate its condition if the level is below the + // minimum. + MOJO_LOG_IF(INFO, NotCalledCondition()) << "hello"; + EXPECT_FALSE(not_called_condition_was_called()); + EXPECT_FALSE(log_message_was_called()); +} + +TEST_F(LoggingTest, Check) { + MOJO_CHECK(true) << "hello"; + EXPECT_FALSE(log_message_was_called()); + + ResetMockLogger(); + + PtrToMemberHelper helper; + helper.member = 0; + int PtrToMemberHelper::*member_ptr = &PtrToMemberHelper::member; + + // Also try to make sure that we parenthesize the condition properly. + MOJO_CHECK(helper.*member_ptr == 1) << "hello"; + EXPECT_TRUE(log_message_was_called()); + EXPECT_EQ(MOJO_LOG_LEVEL_FATAL, last_log_level()); + EXPECT_EQ(kOurFilename, last_source_file()); + EXPECT_EQ(static_cast<uint32_t>(__LINE__ - 4), last_source_line()); + EXPECT_EQ("Check failed: helper.*member_ptr == 1. hello", last_message()); + + ResetMockLogger(); + + // Also test a "naked" |MOJO_CHECK()|s. + MOJO_CHECK(1 + 2 == 3); + EXPECT_FALSE(log_message_was_called()); +} + +TEST_F(LoggingTest, Dlog) { + // We start at |MOJO_LOG_LEVEL_INFO|. + MOJO_DLOG(VERBOSE) << "hello"; + EXPECT_FALSE(log_message_was_called()); + + ResetMockLogger(); + + MOJO_DLOG(INFO) << "hello"; +#ifdef NDEBUG + EXPECT_FALSE(log_message_was_called()); +#else + EXPECT_TRUE(log_message_was_called()); + EXPECT_EQ(MOJO_LOG_LEVEL_INFO, last_log_level()); + EXPECT_EQ(kOurFilename, last_source_file()); + EXPECT_EQ(static_cast<uint32_t>(__LINE__ - 7), last_source_line()); + EXPECT_EQ("hello", last_message()); +#endif +} + +TEST_F(LoggingTest, DlogIf) { + // We start at |MOJO_LOG_LEVEL_INFO|. It shouldn't evaluate the condition in + // this case. + MOJO_DLOG_IF(VERBOSE, NotCalledCondition()) << "hello"; + EXPECT_FALSE(not_called_condition_was_called()); + EXPECT_FALSE(log_message_was_called()); + + ResetMockLogger(); + + MOJO_DLOG_IF(INFO, 1 == 0) << "hello"; + EXPECT_FALSE(log_message_was_called()); + + ResetMockLogger(); + + MOJO_DLOG_IF(INFO, 1 == 1) << "hello"; +#ifdef NDEBUG + EXPECT_FALSE(log_message_was_called()); +#else + EXPECT_TRUE(log_message_was_called()); + EXPECT_EQ(MOJO_LOG_LEVEL_INFO, last_log_level()); + EXPECT_EQ(kOurFilename, last_source_file()); + EXPECT_EQ(static_cast<uint32_t>(__LINE__ - 7), last_source_line()); + EXPECT_EQ("hello", last_message()); +#endif + + ResetMockLogger(); + +// |MOJO_DLOG_IF()| shouldn't compile its condition for non-debug builds. +#ifndef NDEBUG + bool debug_only = true; +#endif + MOJO_DLOG_IF(WARNING, debug_only) << "hello"; +#ifdef NDEBUG + EXPECT_FALSE(log_message_was_called()); +#else + EXPECT_TRUE(log_message_was_called()); + EXPECT_EQ(MOJO_LOG_LEVEL_WARNING, last_log_level()); + EXPECT_EQ(kOurFilename, last_source_file()); + EXPECT_EQ(static_cast<uint32_t>(__LINE__ - 7), last_source_line()); + EXPECT_EQ("hello", last_message()); +#endif +} + +TEST_F(LoggingTest, Dcheck) { + MOJO_DCHECK(true); + EXPECT_FALSE(log_message_was_called()); + + ResetMockLogger(); + + MOJO_DCHECK(true) << "hello"; + EXPECT_FALSE(log_message_was_called()); + + ResetMockLogger(); + + // |MOJO_DCHECK()| should compile (but not evaluate) its condition even for + // non-debug builds. (Hopefully, we'll get an unused variable error if it + // fails to compile the condition.) + bool was_called = false; + MOJO_DCHECK(DcheckTestHelper(&was_called)) << "hello"; +#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON) + EXPECT_FALSE(was_called); + EXPECT_FALSE(log_message_was_called()); +#else + EXPECT_TRUE(was_called); + EXPECT_TRUE(log_message_was_called()); + EXPECT_EQ(MOJO_LOG_LEVEL_FATAL, last_log_level()); + EXPECT_EQ(kOurFilename, last_source_file()); + EXPECT_EQ(static_cast<uint32_t>(__LINE__ - 9), last_source_line()); + EXPECT_EQ("Check failed: DcheckTestHelper(&was_called). hello", + last_message()); +#endif + + ResetMockLogger(); + + // Also try to make sure that we parenthesize the condition properly. + bool x = true; + MOJO_DCHECK(false || x) << "hello"; + EXPECT_FALSE(log_message_was_called()); +} + +} // namespace +} // namespace mojo diff --git a/mojo/public/cpp/system/BUILD.gn b/mojo/public/cpp/system/BUILD.gn new file mode 100644 index 0000000..e120462 --- /dev/null +++ b/mojo/public/cpp/system/BUILD.gn @@ -0,0 +1,19 @@ +# 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. + +import("../../mojo_sdk.gni") + +mojo_sdk_source_set("system") { + sources = [ + "buffer.h", + "core.h", + "data_pipe.h", + "functions.h", + "handle.h", + "macros.h", + "message_pipe.h", + ] + + mojo_sdk_public_deps = [ "mojo/public/c/system" ] +} diff --git a/mojo/public/cpp/system/buffer.h b/mojo/public/cpp/system/buffer.h new file mode 100644 index 0000000..9458c0a --- /dev/null +++ b/mojo/public/cpp/system/buffer.h @@ -0,0 +1,132 @@ +// 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. + +// This file provides a C++ wrapping around the Mojo C API for shared buffers, +// replacing the prefix of "Mojo" with a "mojo" namespace, and using more +// strongly-typed representations of |MojoHandle|s. +// +// Please see "mojo/public/c/system/buffer.h" for complete documentation of the +// API. + +#ifndef MOJO_PUBLIC_CPP_SYSTEM_BUFFER_H_ +#define MOJO_PUBLIC_CPP_SYSTEM_BUFFER_H_ + +#include <assert.h> + +#include "mojo/public/c/system/buffer.h" +#include "mojo/public/cpp/system/handle.h" +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { + +// A strongly-typed representation of a |MojoHandle| referring to a shared +// buffer. +class SharedBufferHandle : public Handle { + public: + SharedBufferHandle() {} + explicit SharedBufferHandle(MojoHandle value) : Handle(value) {} + + // Copying and assignment allowed. +}; + +static_assert(sizeof(SharedBufferHandle) == sizeof(Handle), + "Bad size for C++ SharedBufferHandle"); + +typedef ScopedHandleBase<SharedBufferHandle> ScopedSharedBufferHandle; +static_assert(sizeof(ScopedSharedBufferHandle) == sizeof(SharedBufferHandle), + "Bad size for C++ ScopedSharedBufferHandle"); + +// Creates a shared buffer. See |MojoCreateSharedBuffer()| for complete +// documentation. +inline MojoResult CreateSharedBuffer( + const MojoCreateSharedBufferOptions* options, + uint64_t num_bytes, + ScopedSharedBufferHandle* shared_buffer) { + assert(shared_buffer); + SharedBufferHandle handle; + MojoResult rv = + MojoCreateSharedBuffer(options, num_bytes, handle.mutable_value()); + // Reset even on failure (reduces the chances that a "stale"/incorrect handle + // will be used). + shared_buffer->reset(handle); + return rv; +} + +// Duplicates a handle to a buffer, most commonly so that the buffer can be +// shared with other applications. See |MojoDuplicateBufferHandle()| for +// complete documentation. +// +// TODO(ggowan): Rename this to DuplicateBufferHandle since it is making another +// handle to the same buffer, not duplicating the buffer itself. +// +// TODO(vtl): This (and also the functions below) are templatized to allow for +// future/other buffer types. A bit "safer" would be to overload this function +// manually. (The template enforces that the in and out handles be of the same +// type.) +template <class BufferHandleType> +inline MojoResult DuplicateBuffer( + BufferHandleType buffer, + const MojoDuplicateBufferHandleOptions* options, + ScopedHandleBase<BufferHandleType>* new_buffer) { + assert(new_buffer); + BufferHandleType handle; + MojoResult rv = MojoDuplicateBufferHandle( + buffer.value(), options, handle.mutable_value()); + // Reset even on failure (reduces the chances that a "stale"/incorrect handle + // will be used). + new_buffer->reset(handle); + return rv; +} + +// Maps a part of a buffer (specified by |buffer|, |offset|, and |num_bytes|) +// into memory. See |MojoMapBuffer()| for complete documentation. +template <class BufferHandleType> +inline MojoResult MapBuffer(BufferHandleType buffer, + uint64_t offset, + uint64_t num_bytes, + void** pointer, + MojoMapBufferFlags flags) { + assert(buffer.is_valid()); + return MojoMapBuffer(buffer.value(), offset, num_bytes, pointer, flags); +} + +// Unmaps a part of a buffer that was previously mapped with |MapBuffer()|. +// See |MojoUnmapBuffer()| for complete documentation. +inline MojoResult UnmapBuffer(void* pointer) { + assert(pointer); + return MojoUnmapBuffer(pointer); +} + +// A wrapper class that automatically creates a shared buffer and owns the +// handle. +class SharedBuffer { + public: + explicit SharedBuffer(uint64_t num_bytes); + SharedBuffer(uint64_t num_bytes, + const MojoCreateSharedBufferOptions& options); + ~SharedBuffer(); + + ScopedSharedBufferHandle handle; +}; + +inline SharedBuffer::SharedBuffer(uint64_t num_bytes) { + MojoResult result = CreateSharedBuffer(nullptr, num_bytes, &handle); + MOJO_ALLOW_UNUSED_LOCAL(result); + assert(result == MOJO_RESULT_OK); +} + +inline SharedBuffer::SharedBuffer( + uint64_t num_bytes, + const MojoCreateSharedBufferOptions& options) { + MojoResult result = CreateSharedBuffer(&options, num_bytes, &handle); + MOJO_ALLOW_UNUSED_LOCAL(result); + assert(result == MOJO_RESULT_OK); +} + +inline SharedBuffer::~SharedBuffer() { +} + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_SYSTEM_BUFFER_H_ diff --git a/mojo/public/cpp/system/core.h b/mojo/public/cpp/system/core.h new file mode 100644 index 0000000..b08a5a6 --- /dev/null +++ b/mojo/public/cpp/system/core.h @@ -0,0 +1,15 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_SYSTEM_CORE_H_ +#define MOJO_PUBLIC_CPP_SYSTEM_CORE_H_ + +#include "mojo/public/cpp/system/buffer.h" +#include "mojo/public/cpp/system/data_pipe.h" +#include "mojo/public/cpp/system/functions.h" +#include "mojo/public/cpp/system/handle.h" +#include "mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/system/message_pipe.h" + +#endif // MOJO_PUBLIC_CPP_SYSTEM_CORE_H_ diff --git a/mojo/public/cpp/system/data_pipe.h b/mojo/public/cpp/system/data_pipe.h new file mode 100644 index 0000000..c451cff --- /dev/null +++ b/mojo/public/cpp/system/data_pipe.h @@ -0,0 +1,162 @@ +// 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. + +// This file provides a C++ wrapping around the Mojo C API for data pipes, +// replacing the prefix of "Mojo" with a "mojo" namespace, and using more +// strongly-typed representations of |MojoHandle|s. +// +// Please see "mojo/public/c/system/data_pipe.h" for complete documentation of +// the API. + +#ifndef MOJO_PUBLIC_CPP_SYSTEM_DATA_PIPE_H_ +#define MOJO_PUBLIC_CPP_SYSTEM_DATA_PIPE_H_ + +#include <assert.h> + +#include "mojo/public/c/system/data_pipe.h" +#include "mojo/public/cpp/system/handle.h" +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { + +// A strongly-typed representation of a |MojoHandle| to the producer end of a +// data pipe. +class DataPipeProducerHandle : public Handle { + public: + DataPipeProducerHandle() {} + explicit DataPipeProducerHandle(MojoHandle value) : Handle(value) {} + + // Copying and assignment allowed. +}; + +static_assert(sizeof(DataPipeProducerHandle) == sizeof(Handle), + "Bad size for C++ DataPipeProducerHandle"); + +typedef ScopedHandleBase<DataPipeProducerHandle> ScopedDataPipeProducerHandle; +static_assert(sizeof(ScopedDataPipeProducerHandle) == + sizeof(DataPipeProducerHandle), + "Bad size for C++ ScopedDataPipeProducerHandle"); + +// A strongly-typed representation of a |MojoHandle| to the consumer end of a +// data pipe. +class DataPipeConsumerHandle : public Handle { + public: + DataPipeConsumerHandle() {} + explicit DataPipeConsumerHandle(MojoHandle value) : Handle(value) {} + + // Copying and assignment allowed. +}; + +static_assert(sizeof(DataPipeConsumerHandle) == sizeof(Handle), + "Bad size for C++ DataPipeConsumerHandle"); + +typedef ScopedHandleBase<DataPipeConsumerHandle> ScopedDataPipeConsumerHandle; +static_assert(sizeof(ScopedDataPipeConsumerHandle) == + sizeof(DataPipeConsumerHandle), + "Bad size for C++ ScopedDataPipeConsumerHandle"); + +// Creates a new data pipe. See |MojoCreateDataPipe()| for complete +// documentation. +inline MojoResult CreateDataPipe( + const MojoCreateDataPipeOptions* options, + ScopedDataPipeProducerHandle* data_pipe_producer, + ScopedDataPipeConsumerHandle* data_pipe_consumer) { + assert(data_pipe_producer); + assert(data_pipe_consumer); + DataPipeProducerHandle producer_handle; + DataPipeConsumerHandle consumer_handle; + MojoResult rv = MojoCreateDataPipe(options, + producer_handle.mutable_value(), + consumer_handle.mutable_value()); + // Reset even on failure (reduces the chances that a "stale"/incorrect handle + // will be used). + data_pipe_producer->reset(producer_handle); + data_pipe_consumer->reset(consumer_handle); + return rv; +} + +// Writes to a data pipe. See |MojoWriteData| for complete documentation. +inline MojoResult WriteDataRaw(DataPipeProducerHandle data_pipe_producer, + const void* elements, + uint32_t* num_bytes, + MojoWriteDataFlags flags) { + return MojoWriteData(data_pipe_producer.value(), elements, num_bytes, flags); +} + +// Begins a two-phase write to a data pipe. See |MojoBeginWriteData()| for +// complete documentation. +inline MojoResult BeginWriteDataRaw(DataPipeProducerHandle data_pipe_producer, + void** buffer, + uint32_t* buffer_num_bytes, + MojoWriteDataFlags flags) { + return MojoBeginWriteData( + data_pipe_producer.value(), buffer, buffer_num_bytes, flags); +} + +// Completes a two-phase write to a data pipe. See |MojoEndWriteData()| for +// complete documentation. +inline MojoResult EndWriteDataRaw(DataPipeProducerHandle data_pipe_producer, + uint32_t num_bytes_written) { + return MojoEndWriteData(data_pipe_producer.value(), num_bytes_written); +} + +// Reads from a data pipe. See |MojoReadData()| for complete documentation. +inline MojoResult ReadDataRaw(DataPipeConsumerHandle data_pipe_consumer, + void* elements, + uint32_t* num_bytes, + MojoReadDataFlags flags) { + return MojoReadData(data_pipe_consumer.value(), elements, num_bytes, flags); +} + +// Begins a two-phase read from a data pipe. See |MojoBeginReadData()| for +// complete documentation. +inline MojoResult BeginReadDataRaw(DataPipeConsumerHandle data_pipe_consumer, + const void** buffer, + uint32_t* buffer_num_bytes, + MojoReadDataFlags flags) { + return MojoBeginReadData( + data_pipe_consumer.value(), buffer, buffer_num_bytes, flags); +} + +// Completes a two-phase read from a data pipe. See |MojoEndReadData()| for +// complete documentation. +inline MojoResult EndReadDataRaw(DataPipeConsumerHandle data_pipe_consumer, + uint32_t num_bytes_read) { + return MojoEndReadData(data_pipe_consumer.value(), num_bytes_read); +} + +// A wrapper class that automatically creates a data pipe and owns both handles. +// TODO(vtl): Make an even more friendly version? (Maybe templatized for a +// particular type instead of some "element"? Maybe functions that take +// vectors?) +class DataPipe { + public: + DataPipe(); + explicit DataPipe(const MojoCreateDataPipeOptions& options); + ~DataPipe(); + + ScopedDataPipeProducerHandle producer_handle; + ScopedDataPipeConsumerHandle consumer_handle; +}; + +inline DataPipe::DataPipe() { + MojoResult result = + CreateDataPipe(nullptr, &producer_handle, &consumer_handle); + MOJO_ALLOW_UNUSED_LOCAL(result); + assert(result == MOJO_RESULT_OK); +} + +inline DataPipe::DataPipe(const MojoCreateDataPipeOptions& options) { + MojoResult result = + CreateDataPipe(&options, &producer_handle, &consumer_handle); + MOJO_ALLOW_UNUSED_LOCAL(result); + assert(result == MOJO_RESULT_OK); +} + +inline DataPipe::~DataPipe() { +} + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_SYSTEM_DATA_PIPE_H_ diff --git a/mojo/public/cpp/system/functions.h b/mojo/public/cpp/system/functions.h new file mode 100644 index 0000000..9cfe316 --- /dev/null +++ b/mojo/public/cpp/system/functions.h @@ -0,0 +1,32 @@ +// 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. + +// This file provides a C++ wrapping around the standalone functions of the Mojo +// C API, replacing the prefix of "Mojo" with a "mojo" namespace. +// +// Please see "mojo/public/c/system/functions.h" for complete documentation of +// the API. + +#ifndef MOJO_PUBLIC_CPP_SYSTEM_FUNCTIONS_H_ +#define MOJO_PUBLIC_CPP_SYSTEM_FUNCTIONS_H_ + +#include "mojo/public/c/system/functions.h" + +namespace mojo { + +// Returns the current |MojoTimeTicks| value. See |MojoGetTimeTicksNow()| for +// complete documentation. +inline MojoTimeTicks GetTimeTicksNow() { + return MojoGetTimeTicksNow(); +} + +// The C++ wrappers for |MojoWait()| and |MojoWaitMany()| are defined in +// "handle.h". +// TODO(ggowan): Consider making the C and C++ APIs more consistent in the +// organization of the functions into different header files (since in the C +// API, those functions are defined in "functions.h"). + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_SYSTEM_FUNCTIONS_H_ diff --git a/mojo/public/cpp/system/handle.h b/mojo/public/cpp/system/handle.h new file mode 100644 index 0000000..45624bc --- /dev/null +++ b/mojo/public/cpp/system/handle.h @@ -0,0 +1,290 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_SYSTEM_HANDLE_H_ +#define MOJO_PUBLIC_CPP_SYSTEM_HANDLE_H_ + +#include <assert.h> +#include <limits> + +#include "mojo/public/c/system/functions.h" +#include "mojo/public/c/system/types.h" +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { + +// OVERVIEW +// +// |Handle| and |...Handle|: +// +// |Handle| is a simple, copyable wrapper for the C type |MojoHandle| (which is +// just an integer). Its purpose is to increase type-safety, not provide +// lifetime management. For the same purpose, we have trivial *subclasses* of +// |Handle|, e.g., |MessagePipeHandle| and |DataPipeProducerHandle|. |Handle| +// and its subclasses impose *no* extra overhead over using |MojoHandle|s +// directly. +// +// Note that though we provide constructors for |Handle|/|...Handle| from a +// |MojoHandle|, we do not provide, e.g., a constructor for |MessagePipeHandle| +// from a |Handle|. This is for type safety: If we did, you'd then be able to +// construct a |MessagePipeHandle| from, e.g., a |DataPipeProducerHandle| (since +// it's a |Handle|). +// +// |ScopedHandleBase| and |Scoped...Handle|: +// +// |ScopedHandleBase<HandleType>| is a templated scoped wrapper, for the handle +// types above (in the same sense that a C++11 |unique_ptr<T>| is a scoped +// wrapper for a |T*|). It provides lifetime management, closing its owned +// handle on destruction. It also provides (emulated) move semantics, again +// along the lines of C++11's |unique_ptr| (and exactly like Chromium's +// |scoped_ptr|). +// +// |ScopedHandle| is just (a typedef of) a |ScopedHandleBase<Handle>|. +// Similarly, |ScopedMessagePipeHandle| is just a +// |ScopedHandleBase<MessagePipeHandle>|. Etc. Note that a +// |ScopedMessagePipeHandle| is *not* a (subclass of) |ScopedHandle|. +// +// Wrapper functions: +// +// We provide simple wrappers for the |Mojo...()| functions (in +// mojo/public/c/system/core.h -- see that file for details on individual +// functions). +// +// The general guideline is functions that imply ownership transfer of a handle +// should take (or produce) an appropriate |Scoped...Handle|, while those that +// don't take a |...Handle|. For example, |CreateMessagePipe()| has two +// |ScopedMessagePipe| "out" parameters, whereas |Wait()| and |WaitMany()| take +// |Handle| parameters. Some, have both: e.g., |DuplicatedBuffer()| takes a +// suitable (unscoped) handle (e.g., |SharedBufferHandle|) "in" parameter and +// produces a suitable scoped handle (e.g., |ScopedSharedBufferHandle| a.k.a. +// |ScopedHandleBase<SharedBufferHandle>|) as an "out" parameter. +// +// An exception are some of the |...Raw()| functions. E.g., |CloseRaw()| takes a +// |Handle|, leaving the user to discard the wrapper. +// +// ScopedHandleBase ------------------------------------------------------------ + +// Scoper for the actual handle types defined further below. It's move-only, +// like the C++11 |unique_ptr|. +template <class HandleType> +class ScopedHandleBase { + MOJO_MOVE_ONLY_TYPE(ScopedHandleBase) + + public: + ScopedHandleBase() {} + explicit ScopedHandleBase(HandleType handle) : handle_(handle) {} + ~ScopedHandleBase() { CloseIfNecessary(); } + + template <class CompatibleHandleType> + explicit ScopedHandleBase(ScopedHandleBase<CompatibleHandleType> other) + : handle_(other.release()) {} + + // Move-only constructor and operator=. + ScopedHandleBase(ScopedHandleBase&& other) : handle_(other.release()) {} + ScopedHandleBase& operator=(ScopedHandleBase&& other) { + if (&other != this) { + CloseIfNecessary(); + handle_ = other.release(); + } + return *this; + } + + const HandleType& get() const { return handle_; } + + template <typename PassedHandleType> + static ScopedHandleBase<HandleType> From( + ScopedHandleBase<PassedHandleType> other) { + static_assert( + sizeof(static_cast<PassedHandleType*>(static_cast<HandleType*>(0))), + "HandleType is not a subtype of PassedHandleType"); + return ScopedHandleBase<HandleType>( + static_cast<HandleType>(other.release().value())); + } + + void swap(ScopedHandleBase& other) { handle_.swap(other.handle_); } + + HandleType release() MOJO_WARN_UNUSED_RESULT { + HandleType rv; + rv.swap(handle_); + return rv; + } + + void reset(HandleType handle = HandleType()) { + CloseIfNecessary(); + handle_ = handle; + } + + bool is_valid() const { return handle_.is_valid(); } + + private: + void CloseIfNecessary() { + if (!handle_.is_valid()) + return; + MojoResult result = MojoClose(handle_.value()); + MOJO_ALLOW_UNUSED_LOCAL(result); + assert(result == MOJO_RESULT_OK); + } + + HandleType handle_; +}; + +template <typename HandleType> +inline ScopedHandleBase<HandleType> MakeScopedHandle(HandleType handle) { + return ScopedHandleBase<HandleType>(handle); +} + +// Handle ---------------------------------------------------------------------- + +const MojoHandle kInvalidHandleValue = MOJO_HANDLE_INVALID; + +// Wrapper base class for |MojoHandle|. +class Handle { + public: + Handle() : value_(kInvalidHandleValue) {} + explicit Handle(MojoHandle value) : value_(value) {} + ~Handle() {} + + void swap(Handle& other) { + MojoHandle temp = value_; + value_ = other.value_; + other.value_ = temp; + } + + bool is_valid() const { return value_ != kInvalidHandleValue; } + + const MojoHandle& value() const { return value_; } + MojoHandle* mutable_value() { return &value_; } + void set_value(MojoHandle value) { value_ = value; } + + private: + MojoHandle value_; + + // Copying and assignment allowed. +}; + +// Should have zero overhead. +static_assert(sizeof(Handle) == sizeof(MojoHandle), "Bad size for C++ Handle"); + +// The scoper should also impose no more overhead. +typedef ScopedHandleBase<Handle> ScopedHandle; +static_assert(sizeof(ScopedHandle) == sizeof(Handle), + "Bad size for C++ ScopedHandle"); + +inline MojoResult Wait(Handle handle, + MojoHandleSignals signals, + MojoDeadline deadline, + MojoHandleSignalsState* signals_state) { + return MojoWait(handle.value(), signals, deadline, signals_state); +} + +const uint32_t kInvalidWaitManyIndexValue = static_cast<uint32_t>(-1); + +// Simplify the interpretation of the output from |MojoWaitMany()|. +class WaitManyResult { + public: + explicit WaitManyResult(MojoResult mojo_wait_many_result) + : result(mojo_wait_many_result), index(kInvalidWaitManyIndexValue) {} + + WaitManyResult(MojoResult mojo_wait_many_result, uint32_t result_index) + : result(mojo_wait_many_result), index(result_index) {} + + // A valid handle index is always returned if |WaitMany()| succeeds, but may + // or may not be returned if |WaitMany()| returns an error. Use this helper + // function to check if |index| is a valid index into the handle array. + bool IsIndexValid() const { return index != kInvalidWaitManyIndexValue; } + + // The |signals_states| array is always returned by |WaitMany()| on success, + // but may or may not be returned if |WaitMany()| returns an error. Use this + // helper function to check if |signals_states| holds valid data. + bool AreSignalsStatesValid() const { + return result != MOJO_RESULT_INVALID_ARGUMENT && + result != MOJO_RESULT_RESOURCE_EXHAUSTED; + } + + MojoResult result; + uint32_t index; +}; + +// |HandleVectorType| and |FlagsVectorType| should be similar enough to +// |std::vector<Handle>| and |std::vector<MojoHandleSignals>|, respectively: +// - They should have a (const) |size()| method that returns an unsigned type. +// - They must provide contiguous storage, with access via (const) reference to +// that storage provided by a (const) |operator[]()| (by reference). +template <class HandleVectorType, + class FlagsVectorType, + class SignalsStateVectorType> +inline WaitManyResult WaitMany(const HandleVectorType& handles, + const FlagsVectorType& signals, + MojoDeadline deadline, + SignalsStateVectorType* signals_states) { + if (signals.size() != handles.size() || + (signals_states && signals_states->size() != signals.size())) + return WaitManyResult(MOJO_RESULT_INVALID_ARGUMENT); + if (handles.size() >= kInvalidWaitManyIndexValue) + return WaitManyResult(MOJO_RESULT_RESOURCE_EXHAUSTED); + + if (handles.size() == 0) { + return WaitManyResult( + MojoWaitMany(nullptr, nullptr, 0, deadline, nullptr, nullptr)); + } + + uint32_t result_index = kInvalidWaitManyIndexValue; + const Handle& first_handle = handles[0]; + const MojoHandleSignals& first_signals = signals[0]; + MojoHandleSignalsState* first_state = + signals_states ? &(*signals_states)[0] : nullptr; + MojoResult result = + MojoWaitMany(reinterpret_cast<const MojoHandle*>(&first_handle), + &first_signals, static_cast<uint32_t>(handles.size()), + deadline, &result_index, first_state); + return WaitManyResult(result, result_index); +} + +// C++ 4.10, regarding pointer conversion, says that an integral null pointer +// constant can be converted to |std::nullptr_t| (which is a typedef for +// |decltype(nullptr)|). The opposite direction is not allowed. +template <class HandleVectorType, class FlagsVectorType> +inline WaitManyResult WaitMany(const HandleVectorType& handles, + const FlagsVectorType& signals, + MojoDeadline deadline, + decltype(nullptr) signals_states) { + if (signals.size() != handles.size()) + return WaitManyResult(MOJO_RESULT_INVALID_ARGUMENT); + if (handles.size() >= kInvalidWaitManyIndexValue) + return WaitManyResult(MOJO_RESULT_RESOURCE_EXHAUSTED); + + if (handles.size() == 0) { + return WaitManyResult( + MojoWaitMany(nullptr, nullptr, 0, deadline, nullptr, nullptr)); + } + + uint32_t result_index = kInvalidWaitManyIndexValue; + const Handle& first_handle = handles[0]; + const MojoHandleSignals& first_signals = signals[0]; + MojoResult result = MojoWaitMany( + reinterpret_cast<const MojoHandle*>(&first_handle), &first_signals, + static_cast<uint32_t>(handles.size()), deadline, &result_index, nullptr); + return WaitManyResult(result, result_index); +} + +// |Close()| takes ownership of the handle, since it'll invalidate it. +// Note: There's nothing to do, since the argument will be destroyed when it +// goes out of scope. +template <class HandleType> +inline void Close(ScopedHandleBase<HandleType> /*handle*/) { +} + +// Most users should typically use |Close()| (above) instead. +inline MojoResult CloseRaw(Handle handle) { + return MojoClose(handle.value()); +} + +// Strict weak ordering, so that |Handle|s can be used as keys in |std::map|s, +inline bool operator<(const Handle a, const Handle b) { + return a.value() < b.value(); +} + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_SYSTEM_HANDLE_H_ diff --git a/mojo/public/cpp/system/macros.h b/mojo/public/cpp/system/macros.h new file mode 100644 index 0000000..7f0fcd7 --- /dev/null +++ b/mojo/public/cpp/system/macros.h @@ -0,0 +1,81 @@ +// 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. + +// Define a set of C++ specific macros. +// Mojo C++ API users can assume that mojo/public/cpp/system/macros.h +// includes mojo/public/c/system/macros.h. + +#ifndef MOJO_PUBLIC_CPP_SYSTEM_MACROS_H_ +#define MOJO_PUBLIC_CPP_SYSTEM_MACROS_H_ + +#include "mojo/public/c/system/macros.h" // Symbols exposed. + +// A macro to disallow the copy constructor and operator= functions. +#define MOJO_DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&) = delete; \ + void operator=(const TypeName&) = delete + +// Used to calculate the number of elements in an array. +// (See |arraysize()| in Chromium's base/macros.h for more details.) +namespace mojo { +namespace internal { +template <typename T, size_t N> +char(&ArraySizeHelper(T(&array)[N]))[N]; +#if !defined(_MSC_VER) +template <typename T, size_t N> +char(&ArraySizeHelper(const T(&array)[N]))[N]; +#endif +} // namespace internal +} // namespace mojo +#define MOJO_ARRAYSIZE(array) (sizeof(::mojo::internal::ArraySizeHelper(array))) + +// Used to make a type move-only. See Chromium's base/move.h for more +// details. The MoveOnlyTypeForCPP03 typedef is for Chromium's base/callback.h +// to tell that this type is move-only. +#define MOJO_MOVE_ONLY_TYPE(type) \ + private: \ + type(type&); \ + void operator=(type&); \ + \ + public: \ + type&& Pass() MOJO_WARN_UNUSED_RESULT { return static_cast<type&&>(*this); } \ + typedef void MoveOnlyTypeForCPP03; \ + \ + private: + +// The C++ standard requires that static const members have an out-of-class +// definition (in a single compilation unit), but MSVC chokes on this (when +// language extensions, which are required, are enabled). (You're only likely to +// notice the need for a definition if you take the address of the member or, +// more commonly, pass it to a function that takes it as a reference argument -- +// probably an STL function.) This macro makes MSVC do the right thing. See +// http://msdn.microsoft.com/en-us/library/34h23df8(v=vs.100).aspx for more +// information. This workaround does not appear to be necessary after VS2015. +// Use like: +// +// In the .h file: +// struct Foo { +// static const int kBar = 5; +// }; +// +// In the .cc file: +// MOJO_STATIC_CONST_MEMBER_DEFINITION const int Foo::kBar; +#if defined(_MSC_VER) && _MSC_VER < 1900 +#define MOJO_STATIC_CONST_MEMBER_DEFINITION __declspec(selectany) +#else +#define MOJO_STATIC_CONST_MEMBER_DEFINITION +#endif + +namespace mojo { + +// Used to explicitly mark the return value of a function as unused. (You this +// if you are really sure you don't want to do anything with the return value of +// a function marked with |MOJO_WARN_UNUSED_RESULT|. +template <typename T> +inline void ignore_result(const T&) { +} + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_SYSTEM_MACROS_H_ diff --git a/mojo/public/cpp/system/message_pipe.h b/mojo/public/cpp/system/message_pipe.h new file mode 100644 index 0000000..e7a1e35 --- /dev/null +++ b/mojo/public/cpp/system/message_pipe.h @@ -0,0 +1,119 @@ +// 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. + +// This file provides a C++ wrapping around the Mojo C API for message pipes, +// replacing the prefix of "Mojo" with a "mojo" namespace, and using more +// strongly-typed representations of |MojoHandle|s. +// +// Please see "mojo/public/c/system/message_pipe.h" for complete documentation +// of the API. + +#ifndef MOJO_PUBLIC_CPP_SYSTEM_MESSAGE_PIPE_H_ +#define MOJO_PUBLIC_CPP_SYSTEM_MESSAGE_PIPE_H_ + +#include <assert.h> + +#include "mojo/public/c/system/message_pipe.h" +#include "mojo/public/cpp/system/handle.h" +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { + +// A strongly-typed representation of a |MojoHandle| to one end of a message +// pipe. +class MessagePipeHandle : public Handle { + public: + MessagePipeHandle() {} + explicit MessagePipeHandle(MojoHandle value) : Handle(value) {} + + // Copying and assignment allowed. +}; + +static_assert(sizeof(MessagePipeHandle) == sizeof(Handle), + "Bad size for C++ MessagePipeHandle"); + +typedef ScopedHandleBase<MessagePipeHandle> ScopedMessagePipeHandle; +static_assert(sizeof(ScopedMessagePipeHandle) == sizeof(MessagePipeHandle), + "Bad size for C++ ScopedMessagePipeHandle"); + +// Creates a message pipe. See |MojoCreateMessagePipe()| for complete +// documentation. +inline MojoResult CreateMessagePipe(const MojoCreateMessagePipeOptions* options, + ScopedMessagePipeHandle* message_pipe0, + ScopedMessagePipeHandle* message_pipe1) { + assert(message_pipe0); + assert(message_pipe1); + MessagePipeHandle handle0; + MessagePipeHandle handle1; + MojoResult rv = MojoCreateMessagePipe( + options, handle0.mutable_value(), handle1.mutable_value()); + // Reset even on failure (reduces the chances that a "stale"/incorrect handle + // will be used). + message_pipe0->reset(handle0); + message_pipe1->reset(handle1); + return rv; +} + +// The following "...Raw" versions fully expose the underlying API, and don't +// help with ownership of handles (especially when writing messages). It is +// expected that in most cases these methods will be called through generated +// bindings anyway. +// TODO(vtl): Write friendlier versions of these functions (using scoped +// handles and/or vectors) if there is a demonstrated need for them. + +// Writes to a message pipe. If handles are attached, on success the handles +// will no longer be valid (the receiver will receive equivalent, but logically +// different, handles). See |MojoWriteMessage()| for complete documentation. +inline MojoResult WriteMessageRaw(MessagePipeHandle message_pipe, + const void* bytes, + uint32_t num_bytes, + const MojoHandle* handles, + uint32_t num_handles, + MojoWriteMessageFlags flags) { + return MojoWriteMessage( + message_pipe.value(), bytes, num_bytes, handles, num_handles, flags); +} + +// Reads from a message pipe. See |MojoReadMessage()| for complete +// documentation. +inline MojoResult ReadMessageRaw(MessagePipeHandle message_pipe, + void* bytes, + uint32_t* num_bytes, + MojoHandle* handles, + uint32_t* num_handles, + MojoReadMessageFlags flags) { + return MojoReadMessage( + message_pipe.value(), bytes, num_bytes, handles, num_handles, flags); +} + +// A wrapper class that automatically creates a message pipe and owns both +// handles. +class MessagePipe { + public: + MessagePipe(); + explicit MessagePipe(const MojoCreateMessagePipeOptions& options); + ~MessagePipe(); + + ScopedMessagePipeHandle handle0; + ScopedMessagePipeHandle handle1; +}; + +inline MessagePipe::MessagePipe() { + MojoResult result = CreateMessagePipe(nullptr, &handle0, &handle1); + MOJO_ALLOW_UNUSED_LOCAL(result); + assert(result == MOJO_RESULT_OK); +} + +inline MessagePipe::MessagePipe(const MojoCreateMessagePipeOptions& options) { + MojoResult result = CreateMessagePipe(&options, &handle0, &handle1); + MOJO_ALLOW_UNUSED_LOCAL(result); + assert(result == MOJO_RESULT_OK); +} + +inline MessagePipe::~MessagePipe() { +} + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_SYSTEM_MESSAGE_PIPE_H_ diff --git a/mojo/public/cpp/system/tests/BUILD.gn b/mojo/public/cpp/system/tests/BUILD.gn new file mode 100644 index 0000000..3b6058c --- /dev/null +++ b/mojo/public/cpp/system/tests/BUILD.gn @@ -0,0 +1,25 @@ +# 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. + +import("../../../mojo_sdk.gni") + +mojo_sdk_source_set("tests") { + testonly = true + + sources = [ + "core_unittest.cc", + "macros_unittest.cc", + ] + + deps = [ + "//testing/gtest", + ] + + mojo_sdk_deps = [ + "mojo/public/c/system/tests", + "mojo/public/cpp/environment:standalone", + "mojo/public/cpp/system", + "mojo/public/cpp/test_support:test_utils", + ] +} diff --git a/mojo/public/cpp/system/tests/core_unittest.cc b/mojo/public/cpp/system/tests/core_unittest.cc new file mode 100644 index 0000000..4dcad43 --- /dev/null +++ b/mojo/public/cpp/system/tests/core_unittest.cc @@ -0,0 +1,495 @@ +// 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. + +// This file tests the C++ Mojo system core wrappers. +// TODO(vtl): Maybe rename "CoreCppTest" -> "CoreTest" if/when this gets +// compiled into a different binary from the C API tests. + +#include "mojo/public/cpp/system/core.h" + +#include <stddef.h> + +#include <map> + +#include "mojo/public/cpp/system/macros.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace { + +const MojoHandleSignals kSignalReadableWritable = + MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE; + +const MojoHandleSignals kSignalAll = MOJO_HANDLE_SIGNAL_READABLE | + MOJO_HANDLE_SIGNAL_WRITABLE | + MOJO_HANDLE_SIGNAL_PEER_CLOSED; + +TEST(CoreCppTest, GetTimeTicksNow) { + const MojoTimeTicks start = GetTimeTicksNow(); + EXPECT_NE(static_cast<MojoTimeTicks>(0), start) + << "GetTimeTicksNow should return nonzero value"; +} + +TEST(CoreCppTest, Basic) { + // Basic |Handle| implementation: + { + EXPECT_EQ(MOJO_HANDLE_INVALID, kInvalidHandleValue); + + Handle h0; + EXPECT_EQ(kInvalidHandleValue, h0.value()); + EXPECT_EQ(kInvalidHandleValue, *h0.mutable_value()); + EXPECT_FALSE(h0.is_valid()); + + Handle h1(static_cast<MojoHandle>(123)); + EXPECT_EQ(static_cast<MojoHandle>(123), h1.value()); + EXPECT_EQ(static_cast<MojoHandle>(123), *h1.mutable_value()); + EXPECT_TRUE(h1.is_valid()); + *h1.mutable_value() = static_cast<MojoHandle>(456); + EXPECT_EQ(static_cast<MojoHandle>(456), h1.value()); + EXPECT_TRUE(h1.is_valid()); + + h1.swap(h0); + EXPECT_EQ(static_cast<MojoHandle>(456), h0.value()); + EXPECT_TRUE(h0.is_valid()); + EXPECT_FALSE(h1.is_valid()); + + h1.set_value(static_cast<MojoHandle>(789)); + h0.swap(h1); + EXPECT_EQ(static_cast<MojoHandle>(789), h0.value()); + EXPECT_TRUE(h0.is_valid()); + EXPECT_EQ(static_cast<MojoHandle>(456), h1.value()); + EXPECT_TRUE(h1.is_valid()); + + // Make sure copy constructor works. + Handle h2(h0); + EXPECT_EQ(static_cast<MojoHandle>(789), h2.value()); + // And assignment. + h2 = h1; + EXPECT_EQ(static_cast<MojoHandle>(456), h2.value()); + + // Make sure that we can put |Handle|s into |std::map|s. + h0 = Handle(static_cast<MojoHandle>(987)); + h1 = Handle(static_cast<MojoHandle>(654)); + h2 = Handle(static_cast<MojoHandle>(321)); + Handle h3; + std::map<Handle, int> handle_to_int; + handle_to_int[h0] = 0; + handle_to_int[h1] = 1; + handle_to_int[h2] = 2; + handle_to_int[h3] = 3; + + EXPECT_EQ(4u, handle_to_int.size()); + EXPECT_FALSE(handle_to_int.find(h0) == handle_to_int.end()); + EXPECT_EQ(0, handle_to_int[h0]); + EXPECT_FALSE(handle_to_int.find(h1) == handle_to_int.end()); + EXPECT_EQ(1, handle_to_int[h1]); + EXPECT_FALSE(handle_to_int.find(h2) == handle_to_int.end()); + EXPECT_EQ(2, handle_to_int[h2]); + EXPECT_FALSE(handle_to_int.find(h3) == handle_to_int.end()); + EXPECT_EQ(3, handle_to_int[h3]); + EXPECT_TRUE(handle_to_int.find(Handle(static_cast<MojoHandle>(13579))) == + handle_to_int.end()); + + // TODO(vtl): With C++11, support |std::unordered_map|s, etc. (Or figure out + // how to support the variations of |hash_map|.) + } + + // |Handle|/|ScopedHandle| functions: + { + ScopedHandle h; + + EXPECT_EQ(kInvalidHandleValue, h.get().value()); + + // This should be a no-op. + Close(h.Pass()); + + // It should still be invalid. + EXPECT_EQ(kInvalidHandleValue, h.get().value()); + + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + Wait(h.get(), ~MOJO_HANDLE_SIGNAL_NONE, 1000000, nullptr)); + + std::vector<Handle> wh; + wh.push_back(h.get()); + std::vector<MojoHandleSignals> sigs; + sigs.push_back(~MOJO_HANDLE_SIGNAL_NONE); + WaitManyResult wait_many_result = + WaitMany(wh, sigs, MOJO_DEADLINE_INDEFINITE, nullptr); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, wait_many_result.result); + EXPECT_TRUE(wait_many_result.IsIndexValid()); + EXPECT_FALSE(wait_many_result.AreSignalsStatesValid()); + + // Make sure that our specialized template correctly handles |NULL| as well + // as |nullptr|. + wait_many_result = WaitMany(wh, sigs, MOJO_DEADLINE_INDEFINITE, NULL); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, wait_many_result.result); + EXPECT_EQ(0u, wait_many_result.index); + EXPECT_TRUE(wait_many_result.IsIndexValid()); + EXPECT_FALSE(wait_many_result.AreSignalsStatesValid()); + } + + // |MakeScopedHandle| (just compilation tests): + { + EXPECT_FALSE(MakeScopedHandle(Handle()).is_valid()); + EXPECT_FALSE(MakeScopedHandle(MessagePipeHandle()).is_valid()); + EXPECT_FALSE(MakeScopedHandle(DataPipeProducerHandle()).is_valid()); + EXPECT_FALSE(MakeScopedHandle(DataPipeConsumerHandle()).is_valid()); + EXPECT_FALSE(MakeScopedHandle(SharedBufferHandle()).is_valid()); + } + + // |MessagePipeHandle|/|ScopedMessagePipeHandle| functions: + { + MessagePipeHandle h_invalid; + EXPECT_FALSE(h_invalid.is_valid()); + EXPECT_EQ( + MOJO_RESULT_INVALID_ARGUMENT, + WriteMessageRaw( + h_invalid, nullptr, 0, nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE)); + char buffer[10] = {0}; + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + WriteMessageRaw(h_invalid, + buffer, + sizeof(buffer), + nullptr, + 0, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + ReadMessageRaw(h_invalid, + nullptr, + nullptr, + nullptr, + nullptr, + MOJO_READ_MESSAGE_FLAG_NONE)); + uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer)); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + ReadMessageRaw(h_invalid, + buffer, + &buffer_size, + nullptr, + nullptr, + MOJO_READ_MESSAGE_FLAG_NONE)); + + // Basic tests of waiting and closing. + MojoHandle hv0 = kInvalidHandleValue; + { + ScopedMessagePipeHandle h0; + ScopedMessagePipeHandle h1; + EXPECT_FALSE(h0.get().is_valid()); + EXPECT_FALSE(h1.get().is_valid()); + + CreateMessagePipe(nullptr, &h0, &h1); + EXPECT_TRUE(h0.get().is_valid()); + EXPECT_TRUE(h1.get().is_valid()); + EXPECT_NE(h0.get().value(), h1.get().value()); + // Save the handle values, so we can check that things got closed + // correctly. + hv0 = h0.get().value(); + MojoHandle hv1 = h1.get().value(); + MojoHandleSignalsState state; + + EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, + Wait(h0.get(), MOJO_HANDLE_SIGNAL_READABLE, 0, &state)); + + EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals); + EXPECT_EQ(kSignalAll, state.satisfiable_signals); + + std::vector<Handle> wh; + wh.push_back(h0.get()); + wh.push_back(h1.get()); + std::vector<MojoHandleSignals> sigs; + sigs.push_back(MOJO_HANDLE_SIGNAL_READABLE); + sigs.push_back(MOJO_HANDLE_SIGNAL_WRITABLE); + std::vector<MojoHandleSignalsState> states(sigs.size()); + WaitManyResult wait_many_result = WaitMany(wh, sigs, 1000, &states); + EXPECT_EQ(MOJO_RESULT_OK, wait_many_result.result); + EXPECT_EQ(1u, wait_many_result.index); + EXPECT_TRUE(wait_many_result.IsIndexValid()); + EXPECT_TRUE(wait_many_result.AreSignalsStatesValid()); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, states[0].satisfied_signals); + EXPECT_EQ(kSignalAll, states[0].satisfiable_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, states[1].satisfied_signals); + EXPECT_EQ(kSignalAll, states[1].satisfiable_signals); + + // Test closing |h1| explicitly. + Close(h1.Pass()); + EXPECT_FALSE(h1.get().is_valid()); + + // Make sure |h1| is closed. + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + Wait(Handle(hv1), ~MOJO_HANDLE_SIGNAL_NONE, + MOJO_DEADLINE_INDEFINITE, nullptr)); + + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + Wait(h0.get(), MOJO_HANDLE_SIGNAL_READABLE, + MOJO_DEADLINE_INDEFINITE, &state)); + + EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfiable_signals); + } + // |hv0| should have been closed when |h0| went out of scope, so this close + // should fail. + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(hv0)); + + // Actually test writing/reading messages. + { + ScopedMessagePipeHandle h0; + ScopedMessagePipeHandle h1; + CreateMessagePipe(nullptr, &h0, &h1); + + const char kHello[] = "hello"; + const uint32_t kHelloSize = static_cast<uint32_t>(sizeof(kHello)); + EXPECT_EQ(MOJO_RESULT_OK, + WriteMessageRaw(h0.get(), + kHello, + kHelloSize, + nullptr, + 0, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + + MojoHandleSignalsState state; + EXPECT_EQ(MOJO_RESULT_OK, Wait(h1.get(), MOJO_HANDLE_SIGNAL_READABLE, + MOJO_DEADLINE_INDEFINITE, &state)); + EXPECT_EQ(kSignalReadableWritable, state.satisfied_signals); + EXPECT_EQ(kSignalAll, state.satisfiable_signals); + + char buffer[10] = {0}; + uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer)); + EXPECT_EQ(MOJO_RESULT_OK, + ReadMessageRaw(h1.get(), + buffer, + &buffer_size, + nullptr, + nullptr, + MOJO_READ_MESSAGE_FLAG_NONE)); + EXPECT_EQ(kHelloSize, buffer_size); + EXPECT_STREQ(kHello, buffer); + + // Send a handle over the previously-establish message pipe. Use the + // |MessagePipe| wrapper (to test it), which automatically creates a + // message pipe. + MessagePipe mp; + + // Write a message to |mp.handle0|, before we send |mp.handle1|. + const char kWorld[] = "world!"; + const uint32_t kWorldSize = static_cast<uint32_t>(sizeof(kWorld)); + EXPECT_EQ(MOJO_RESULT_OK, + WriteMessageRaw(mp.handle0.get(), + kWorld, + kWorldSize, + nullptr, + 0, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + + // Send |mp.handle1| over |h1| to |h0|. + MojoHandle handles[5]; + handles[0] = mp.handle1.release().value(); + EXPECT_NE(kInvalidHandleValue, handles[0]); + EXPECT_FALSE(mp.handle1.get().is_valid()); + uint32_t handles_count = 1; + EXPECT_EQ(MOJO_RESULT_OK, + WriteMessageRaw(h1.get(), + kHello, + kHelloSize, + handles, + handles_count, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + // |handles[0]| should actually be invalid now. + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(handles[0])); + + // Read "hello" and the sent handle. + EXPECT_EQ(MOJO_RESULT_OK, Wait(h0.get(), MOJO_HANDLE_SIGNAL_READABLE, + MOJO_DEADLINE_INDEFINITE, &state)); + EXPECT_EQ(kSignalReadableWritable, state.satisfied_signals); + EXPECT_EQ(kSignalAll, state.satisfiable_signals); + + memset(buffer, 0, sizeof(buffer)); + buffer_size = static_cast<uint32_t>(sizeof(buffer)); + for (size_t i = 0; i < MOJO_ARRAYSIZE(handles); i++) + handles[i] = kInvalidHandleValue; + handles_count = static_cast<uint32_t>(MOJO_ARRAYSIZE(handles)); + EXPECT_EQ(MOJO_RESULT_OK, + ReadMessageRaw(h0.get(), + buffer, + &buffer_size, + handles, + &handles_count, + MOJO_READ_MESSAGE_FLAG_NONE)); + EXPECT_EQ(kHelloSize, buffer_size); + EXPECT_STREQ(kHello, buffer); + EXPECT_EQ(1u, handles_count); + EXPECT_NE(kInvalidHandleValue, handles[0]); + + // Read from the sent/received handle. + mp.handle1.reset(MessagePipeHandle(handles[0])); + // Save |handles[0]| to check that it gets properly closed. + hv0 = handles[0]; + + EXPECT_EQ(MOJO_RESULT_OK, + Wait(mp.handle1.get(), MOJO_HANDLE_SIGNAL_READABLE, + MOJO_DEADLINE_INDEFINITE, &state)); + EXPECT_EQ(kSignalReadableWritable, state.satisfied_signals); + EXPECT_EQ(kSignalAll, state.satisfiable_signals); + + memset(buffer, 0, sizeof(buffer)); + buffer_size = static_cast<uint32_t>(sizeof(buffer)); + for (size_t i = 0; i < MOJO_ARRAYSIZE(handles); i++) + handles[i] = kInvalidHandleValue; + handles_count = static_cast<uint32_t>(MOJO_ARRAYSIZE(handles)); + EXPECT_EQ(MOJO_RESULT_OK, + ReadMessageRaw(mp.handle1.get(), + buffer, + &buffer_size, + handles, + &handles_count, + MOJO_READ_MESSAGE_FLAG_NONE)); + EXPECT_EQ(kWorldSize, buffer_size); + EXPECT_STREQ(kWorld, buffer); + EXPECT_EQ(0u, handles_count); + } + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(hv0)); + } + + // TODO(vtl): Test |CloseRaw()|. + // TODO(vtl): Test |reset()| more thoroughly? +} + +TEST(CoreCppTest, TearDownWithMessagesEnqueued) { + // Tear down a message pipe which still has a message enqueued, with the + // message also having a valid message pipe handle. + { + ScopedMessagePipeHandle h0; + ScopedMessagePipeHandle h1; + CreateMessagePipe(nullptr, &h0, &h1); + + // Send a handle over the previously-establish message pipe. + ScopedMessagePipeHandle h2; + ScopedMessagePipeHandle h3; + CreateMessagePipe(nullptr, &h2, &h3); + + // Write a message to |h2|, before we send |h3|. + const char kWorld[] = "world!"; + const uint32_t kWorldSize = static_cast<uint32_t>(sizeof(kWorld)); + EXPECT_EQ(MOJO_RESULT_OK, + WriteMessageRaw(h2.get(), + kWorld, + kWorldSize, + nullptr, + 0, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + // And also a message to |h3|. + EXPECT_EQ(MOJO_RESULT_OK, + WriteMessageRaw(h3.get(), + kWorld, + kWorldSize, + nullptr, + 0, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + + // Send |h3| over |h1| to |h0|. + const char kHello[] = "hello"; + const uint32_t kHelloSize = static_cast<uint32_t>(sizeof(kHello)); + MojoHandle h3_value; + h3_value = h3.release().value(); + EXPECT_NE(kInvalidHandleValue, h3_value); + EXPECT_FALSE(h3.get().is_valid()); + EXPECT_EQ(MOJO_RESULT_OK, + WriteMessageRaw(h1.get(), + kHello, + kHelloSize, + &h3_value, + 1, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + // |h3_value| should actually be invalid now. + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(h3_value)); + + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h0.release().value())); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h1.release().value())); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h2.release().value())); + } + + // Do this in a different order: make the enqueued message pipe handle only + // half-alive. + { + ScopedMessagePipeHandle h0; + ScopedMessagePipeHandle h1; + CreateMessagePipe(nullptr, &h0, &h1); + + // Send a handle over the previously-establish message pipe. + ScopedMessagePipeHandle h2; + ScopedMessagePipeHandle h3; + CreateMessagePipe(nullptr, &h2, &h3); + + // Write a message to |h2|, before we send |h3|. + const char kWorld[] = "world!"; + const uint32_t kWorldSize = static_cast<uint32_t>(sizeof(kWorld)); + EXPECT_EQ(MOJO_RESULT_OK, + WriteMessageRaw(h2.get(), + kWorld, + kWorldSize, + nullptr, + 0, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + // And also a message to |h3|. + EXPECT_EQ(MOJO_RESULT_OK, + WriteMessageRaw(h3.get(), + kWorld, + kWorldSize, + nullptr, + 0, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + + // Send |h3| over |h1| to |h0|. + const char kHello[] = "hello"; + const uint32_t kHelloSize = static_cast<uint32_t>(sizeof(kHello)); + MojoHandle h3_value; + h3_value = h3.release().value(); + EXPECT_NE(kInvalidHandleValue, h3_value); + EXPECT_FALSE(h3.get().is_valid()); + EXPECT_EQ(MOJO_RESULT_OK, + WriteMessageRaw(h1.get(), + kHello, + kHelloSize, + &h3_value, + 1, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + // |h3_value| should actually be invalid now. + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(h3_value)); + + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h2.release().value())); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h0.release().value())); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h1.release().value())); + } +} + +TEST(CoreCppTest, ScopedHandleMoveCtor) { + ScopedSharedBufferHandle buffer1; + EXPECT_EQ(MOJO_RESULT_OK, CreateSharedBuffer(nullptr, 1024, &buffer1)); + EXPECT_TRUE(buffer1.is_valid()); + + ScopedSharedBufferHandle buffer2; + EXPECT_EQ(MOJO_RESULT_OK, CreateSharedBuffer(nullptr, 1024, &buffer2)); + EXPECT_TRUE(buffer2.is_valid()); + + // If this fails to close buffer1, ScopedHandleBase::CloseIfNecessary() will + // assert. + buffer1 = buffer2.Pass(); + + EXPECT_TRUE(buffer1.is_valid()); + EXPECT_FALSE(buffer2.is_valid()); +} + +TEST(CoreCppTest, ScopedHandleMoveCtorSelf) { + ScopedSharedBufferHandle buffer1; + EXPECT_EQ(MOJO_RESULT_OK, CreateSharedBuffer(nullptr, 1024, &buffer1)); + EXPECT_TRUE(buffer1.is_valid()); + + buffer1 = buffer1.Pass(); + + EXPECT_TRUE(buffer1.is_valid()); +} + +// TODO(vtl): Write data pipe tests. + +} // namespace +} // namespace mojo diff --git a/mojo/public/cpp/system/tests/macros_unittest.cc b/mojo/public/cpp/system/tests/macros_unittest.cc new file mode 100644 index 0000000..72a9b24 --- /dev/null +++ b/mojo/public/cpp/system/tests/macros_unittest.cc @@ -0,0 +1,159 @@ +// 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. + +// This file tests the C++ Mojo system macros and consists of "positive" tests, +// i.e., those verifying that things work (without compile errors, or even +// warnings if warnings are treated as errors). +// TODO(vtl): Maybe rename "MacrosCppTest" -> "MacrosTest" if/when this gets +// compiled into a different binary from the C API tests. +// TODO(vtl): Fix no-compile tests (which are all disabled; crbug.com/105388) +// and write some "negative" tests. + +#include "mojo/public/cpp/system/macros.h" + +#include <assert.h> +#include <stdint.h> +#include <stdlib.h> + +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { + +// The test for |MOJO_STATIC_CONST_MEMBER_DEFINITION| is really a compile/link +// test. To test it fully would really require a header file and multiple .cc +// files, but we'll just cursorily verify it. +// +// This is defined outside of an anonymous namespace because +// MOJO_STATIC_CONST_MEMBER_DEFINITION may not be used on internal symbols. +struct StructWithStaticConstMember { + static const int kStaticConstMember = 123; +}; +MOJO_STATIC_CONST_MEMBER_DEFINITION +const int StructWithStaticConstMember::kStaticConstMember; + +namespace { + +// Note: MSVS is very strict (and arguably buggy) about warnings for classes +// defined in a local scope, so define these globally. +struct TestOverrideBaseClass { + virtual ~TestOverrideBaseClass() {} + virtual void ToBeOverridden() {} + virtual void AlsoToBeOverridden() = 0; +}; + +struct TestOverrideSubclass : public TestOverrideBaseClass { + ~TestOverrideSubclass() override {} + void ToBeOverridden() override {} + void AlsoToBeOverridden() override {} +}; + +TEST(MacrosCppTest, Override) { + TestOverrideSubclass x; + x.ToBeOverridden(); + x.AlsoToBeOverridden(); +} + +// Note: MSVS is very strict (and arguably buggy) about warnings for classes +// defined in a local scope, so define these globally. +class TestDisallowCopyAndAssignClass { + public: + TestDisallowCopyAndAssignClass() {} + explicit TestDisallowCopyAndAssignClass(int) {} + void NoOp() {} + + private: + MOJO_DISALLOW_COPY_AND_ASSIGN(TestDisallowCopyAndAssignClass); +}; + +TEST(MacrosCppTest, DisallowCopyAndAssign) { + TestDisallowCopyAndAssignClass x; + x.NoOp(); + TestDisallowCopyAndAssignClass y(789); + y.NoOp(); +} + +// Test that |MOJO_ARRAYSIZE()| works in a |static_assert()|. +const int kGlobalArray[5] = {1, 2, 3, 4, 5}; +static_assert(MOJO_ARRAYSIZE(kGlobalArray) == 5u, + "MOJO_ARRAY_SIZE() failed in static_assert()"); + +TEST(MacrosCppTest, ArraySize) { + double local_array[4] = {6.7, 7.8, 8.9, 9.0}; + // MSVS considers this local variable unused since MOJO_ARRAYSIZE only takes + // the size of the type of the local and not the values itself. + MOJO_ALLOW_UNUSED_LOCAL(local_array); + EXPECT_EQ(4u, MOJO_ARRAYSIZE(local_array)); +} + +// Note: MSVS is very strict (and arguably buggy) about warnings for classes +// defined in a local scope, so define these globally. +class MoveOnlyInt { + MOJO_MOVE_ONLY_TYPE(MoveOnlyInt) + + public: + MoveOnlyInt() : is_set_(false), value_() {} + explicit MoveOnlyInt(int value) : is_set_(true), value_(value) {} + ~MoveOnlyInt() {} + + // Move-only constructor and operator=. + MoveOnlyInt(MoveOnlyInt&& other) { *this = other.Pass(); } + MoveOnlyInt& operator=(MoveOnlyInt&& other) { + if (&other != this) { + is_set_ = other.is_set_; + value_ = other.value_; + other.is_set_ = false; + } + return *this; + } + + int value() const { + assert(is_set()); + return value_; + } + bool is_set() const { return is_set_; } + + private: + bool is_set_; + int value_; +}; + +TEST(MacrosCppTest, MoveOnlyType) { + MoveOnlyInt x(123); + EXPECT_TRUE(x.is_set()); + EXPECT_EQ(123, x.value()); + MoveOnlyInt y; + EXPECT_FALSE(y.is_set()); + y = x.Pass(); + EXPECT_FALSE(x.is_set()); + EXPECT_TRUE(y.is_set()); + EXPECT_EQ(123, y.value()); + MoveOnlyInt z(y.Pass()); + EXPECT_FALSE(y.is_set()); + EXPECT_TRUE(z.is_set()); + EXPECT_EQ(123, z.value()); + z = z.Pass(); + EXPECT_TRUE(z.is_set()); + EXPECT_EQ(123, z.value()); +} + +// Use it, to make sure things get linked in and to avoid any warnings about +// unused things. +TEST(MacrosCppTest, StaticConstMemberDefinition) { + EXPECT_EQ(123, StructWithStaticConstMember::kStaticConstMember); +} + +// The test for |ignore_result()| is also just a compilation test. (Note that +// |MOJO_WARN_UNUSED_RESULT| can only be used in the prototype. +int ReturnsIntYouMustUse() MOJO_WARN_UNUSED_RESULT; + +int ReturnsIntYouMustUse() { + return 123; +} + +TEST(MacrosCppTest, IgnoreResult) { + ignore_result(ReturnsIntYouMustUse()); +} + +} // namespace +} // namespace mojo diff --git a/mojo/public/cpp/test_support/BUILD.gn b/mojo/public/cpp/test_support/BUILD.gn new file mode 100644 index 0000000..a1f7d31 --- /dev/null +++ b/mojo/public/cpp/test_support/BUILD.gn @@ -0,0 +1,25 @@ +# 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. + +import("../../mojo_sdk.gni") + +# GYP version: mojo/public/mojo_public.gyp:mojo_public_test_utils +mojo_sdk_source_set("test_utils") { + testonly = true + + sources = [ + "lib/test_support.cc", + "lib/test_utils.cc", + "test_utils.h", + ] + + deps = [ + "//testing/gtest", + ] + + mojo_sdk_deps = [ + "mojo/public/c/test_support", + "mojo/public/cpp/system", + ] +} diff --git a/mojo/public/cpp/test_support/lib/test_support.cc b/mojo/public/cpp/test_support/lib/test_support.cc new file mode 100644 index 0000000..0b6035b --- /dev/null +++ b/mojo/public/cpp/test_support/lib/test_support.cc @@ -0,0 +1,26 @@ +// 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. + +#include "mojo/public/cpp/test_support/test_support.h" + +#include <stdlib.h> + +namespace mojo { +namespace test { + +std::vector<std::string> EnumerateSourceRootRelativeDirectory( + const std::string& relative_path) { + char** names = MojoTestSupportEnumerateSourceRootRelativeDirectory( + relative_path.c_str()); + std::vector<std::string> results; + for (char** ptr = names; *ptr != nullptr; ++ptr) { + results.push_back(*ptr); + free(*ptr); + } + free(names); + return results; +} + +} // namespace test +} // namespace mojo diff --git a/mojo/public/cpp/test_support/lib/test_utils.cc b/mojo/public/cpp/test_support/lib/test_utils.cc new file mode 100644 index 0000000..210c6b1 --- /dev/null +++ b/mojo/public/cpp/test_support/lib/test_utils.cc @@ -0,0 +1,97 @@ +// 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. + +#include "mojo/public/cpp/test_support/test_utils.h" + +#include "mojo/public/cpp/system/core.h" +#include "mojo/public/cpp/test_support/test_support.h" + +namespace mojo { +namespace test { + +bool WriteTextMessage(const MessagePipeHandle& handle, + const std::string& text) { + MojoResult rv = WriteMessageRaw(handle, + text.data(), + static_cast<uint32_t>(text.size()), + nullptr, + 0, + MOJO_WRITE_MESSAGE_FLAG_NONE); + return rv == MOJO_RESULT_OK; +} + +bool ReadTextMessage(const MessagePipeHandle& handle, std::string* text) { + MojoResult rv; + bool did_wait = false; + + uint32_t num_bytes = 0, num_handles = 0; + for (;;) { + rv = ReadMessageRaw(handle, + nullptr, + &num_bytes, + nullptr, + &num_handles, + MOJO_READ_MESSAGE_FLAG_NONE); + if (rv == MOJO_RESULT_SHOULD_WAIT) { + if (did_wait) { + assert(false); // Looping endlessly!? + return false; + } + rv = Wait(handle, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE, + nullptr); + if (rv != MOJO_RESULT_OK) + return false; + did_wait = true; + } else { + assert(!num_handles); + break; + } + } + + text->resize(num_bytes); + rv = ReadMessageRaw(handle, + &text->at(0), + &num_bytes, + nullptr, + &num_handles, + MOJO_READ_MESSAGE_FLAG_NONE); + return rv == MOJO_RESULT_OK; +} + +bool DiscardMessage(const MessagePipeHandle& handle) { + MojoResult rv = ReadMessageRaw(handle, + nullptr, + nullptr, + nullptr, + nullptr, + MOJO_READ_MESSAGE_FLAG_MAY_DISCARD); + return rv == MOJO_RESULT_OK; +} + +void IterateAndReportPerf(const char* test_name, + const char* sub_test_name, + PerfTestSingleIteration single_iteration, + void* closure) { + // TODO(vtl): These should be specifiable using command-line flags. + static const size_t kGranularity = 100; + static const MojoTimeTicks kPerftestTimeMicroseconds = 3 * 1000000; + + const MojoTimeTicks start_time = GetTimeTicksNow(); + MojoTimeTicks end_time; + size_t iterations = 0; + do { + for (size_t i = 0; i < kGranularity; i++) + (*single_iteration)(closure); + iterations += kGranularity; + + end_time = GetTimeTicksNow(); + } while (end_time - start_time < kPerftestTimeMicroseconds); + + MojoTestSupportLogPerfResult(test_name, sub_test_name, + 1000000.0 * iterations / (end_time - start_time), + "iterations/second"); +} + +} // namespace test +} // namespace mojo diff --git a/mojo/public/cpp/test_support/test_support.h b/mojo/public/cpp/test_support/test_support.h new file mode 100644 index 0000000..9a536e6 --- /dev/null +++ b/mojo/public/cpp/test_support/test_support.h @@ -0,0 +1,35 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_TEST_SUPPORT_TEST_SUPPORT_H_ +#define MOJO_PUBLIC_CPP_TEST_SUPPORT_TEST_SUPPORT_H_ + +#include <string> +#include <vector> + +#include "mojo/public/c/test_support/test_support.h" + +namespace mojo { +namespace test { + +inline void LogPerfResult(const char* test_name, + const char* sub_test_name, + double value, + const char* units) { + MojoTestSupportLogPerfResult(test_name, sub_test_name, value, units); +} + +// Opens text file relative to the source root for reading. +inline FILE* OpenSourceRootRelativeFile(const std::string& relative_path) { + return MojoTestSupportOpenSourceRootRelativeFile(relative_path.c_str()); +} + +// Returns the list of regular files in a directory relative to the source root. +std::vector<std::string> EnumerateSourceRootRelativeDirectory( + const std::string& relative_path); + +} // namespace test +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_TEST_SUPPORT_TEST_SUPPORT_H_ diff --git a/mojo/public/cpp/test_support/test_utils.h b/mojo/public/cpp/test_support/test_utils.h new file mode 100644 index 0000000..6fd5a9e --- /dev/null +++ b/mojo/public/cpp/test_support/test_utils.h @@ -0,0 +1,40 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_TEST_SUPPORT_TEST_UTILS_H_ +#define MOJO_PUBLIC_CPP_TEST_SUPPORT_TEST_UTILS_H_ + +#include <string> + +#include "mojo/public/cpp/system/core.h" + +namespace mojo { +namespace test { + +// Writes a message to |handle| with message data |text|. Returns true on +// success. +bool WriteTextMessage(const MessagePipeHandle& handle, const std::string& text); + +// Reads a message from |handle|, putting its contents into |*text|. Returns +// true on success. (This blocks if necessary and will call |MojoReadMessage()| +// multiple times, e.g., to query the size of the message.) +bool ReadTextMessage(const MessagePipeHandle& handle, std::string* text); + +// Discards a message from |handle|. Returns true on success. (This does not +// block. It will fail if no message is available to discard.) +bool DiscardMessage(const MessagePipeHandle& handle); + +// Run |single_iteration| an appropriate number of times and report its +// performance appropriately. (This actually runs |single_iteration| for a fixed +// amount of time and reports the number of iterations per unit time.) +typedef void (*PerfTestSingleIteration)(void* closure); +void IterateAndReportPerf(const char* test_name, + const char* sub_test_name, + PerfTestSingleIteration single_iteration, + void* closure); + +} // namespace test +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_TEST_SUPPORT_TEST_UTILS_H_ diff --git a/mojo/public/cpp/utility/BUILD.gn b/mojo/public/cpp/utility/BUILD.gn new file mode 100644 index 0000000..96c1d11 --- /dev/null +++ b/mojo/public/cpp/utility/BUILD.gn @@ -0,0 +1,35 @@ +# 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. + +import("../../mojo_sdk.gni") + +mojo_sdk_source_set("utility") { + sources = [ + "lib/mutex.cc", + "lib/run_loop.cc", + "lib/thread.cc", + "lib/thread_local.h", + "lib/thread_local_posix.cc", + "lib/thread_local_win.cc", + "mutex.h", + "run_loop.h", + "run_loop_handler.h", + "thread.h", + ] + + mojo_sdk_deps = [ + "mojo/public/cpp/bindings:callback", + "mojo/public/cpp/system", + ] + + if (is_win) { + # See crbug.com/342893: + sources -= [ + "lib/mutex.cc", + "lib/thread.cc", + "mutex.h", + "thread.h", + ] + } +} diff --git a/mojo/public/cpp/utility/lib/mutex.cc b/mojo/public/cpp/utility/lib/mutex.cc new file mode 100644 index 0000000..23370e1 --- /dev/null +++ b/mojo/public/cpp/utility/lib/mutex.cc @@ -0,0 +1,52 @@ +// 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. + +#include "mojo/public/cpp/utility/mutex.h" + +#include <assert.h> +#include <errno.h> + +namespace mojo { + +// Release builds have inlined (non-error-checking) definitions in the header. +#if !defined(NDEBUG) +Mutex::Mutex() { + pthread_mutexattr_t mutexattr; + int rv = pthread_mutexattr_init(&mutexattr); + assert(rv == 0); + rv = pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_ERRORCHECK); + assert(rv == 0); + rv = pthread_mutex_init(&mutex_, &mutexattr); + assert(rv == 0); + rv = pthread_mutexattr_destroy(&mutexattr); + assert(rv == 0); +} + +Mutex::~Mutex() { + int rv = pthread_mutex_destroy(&mutex_); + assert(rv == 0); +} + +void Mutex::Lock() { + int rv = pthread_mutex_lock(&mutex_); + assert(rv == 0); +} + +void Mutex::Unlock() { + int rv = pthread_mutex_unlock(&mutex_); + assert(rv == 0); +} + +bool Mutex::TryLock() { + int rv = pthread_mutex_trylock(&mutex_); + assert(rv == 0 || rv == EBUSY); + return rv == 0; +} + +void Mutex::AssertHeld() { + assert(pthread_mutex_lock(&mutex_) == EDEADLK); +} +#endif // !defined(NDEBUG) + +} // namespace mojo diff --git a/mojo/public/cpp/utility/lib/run_loop.cc b/mojo/public/cpp/utility/lib/run_loop.cc new file mode 100644 index 0000000..7faf748 --- /dev/null +++ b/mojo/public/cpp/utility/lib/run_loop.cc @@ -0,0 +1,267 @@ +// 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. + +#include "mojo/public/cpp/utility/run_loop.h" + +#include <assert.h> + +#include <algorithm> +#include <vector> + +#include "mojo/public/cpp/utility/lib/thread_local.h" +#include "mojo/public/cpp/utility/run_loop_handler.h" + +namespace mojo { +namespace { + +internal::ThreadLocalPointer<RunLoop> current_run_loop; + +const MojoTimeTicks kInvalidTimeTicks = static_cast<MojoTimeTicks>(0); + +} // namespace + +// State needed for one iteration of WaitMany(). +struct RunLoop::WaitState { + WaitState() : deadline(MOJO_DEADLINE_INDEFINITE) {} + + std::vector<Handle> handles; + std::vector<MojoHandleSignals> handle_signals; + MojoDeadline deadline; +}; + +struct RunLoop::RunState { + RunState() : should_quit(false) {} + + bool should_quit; +}; + +RunLoop::RunLoop() + : run_state_(nullptr), next_handler_id_(0), next_sequence_number_(0) { + assert(!current()); + current_run_loop.Set(this); +} + +RunLoop::~RunLoop() { + assert(current() == this); + NotifyHandlers(MOJO_RESULT_ABORTED, IGNORE_DEADLINE); + current_run_loop.Set(nullptr); +} + +// static +void RunLoop::SetUp() { + current_run_loop.Allocate(); +} + +// static +void RunLoop::TearDown() { + assert(!current()); + current_run_loop.Free(); +} + +// static +RunLoop* RunLoop::current() { + return current_run_loop.Get(); +} + +void RunLoop::AddHandler(RunLoopHandler* handler, + const Handle& handle, + MojoHandleSignals handle_signals, + MojoDeadline deadline) { + assert(current() == this); + assert(handler); + assert(handle.is_valid()); + // Assume it's an error if someone tries to reregister an existing handle. + assert(0u == handler_data_.count(handle)); + HandlerData handler_data; + handler_data.handler = handler; + handler_data.handle_signals = handle_signals; + handler_data.deadline = + (deadline == MOJO_DEADLINE_INDEFINITE) + ? kInvalidTimeTicks + : GetTimeTicksNow() + static_cast<MojoTimeTicks>(deadline); + handler_data.id = next_handler_id_++; + handler_data_[handle] = handler_data; +} + +void RunLoop::RemoveHandler(const Handle& handle) { + assert(current() == this); + handler_data_.erase(handle); +} + +bool RunLoop::HasHandler(const Handle& handle) const { + return handler_data_.find(handle) != handler_data_.end(); +} + +void RunLoop::Run() { + RunInternal(UNTIL_EMPTY); +} + +void RunLoop::RunUntilIdle() { + RunInternal(UNTIL_IDLE); +} + +void RunLoop::RunInternal(RunMode run_mode) { + assert(current() == this); + RunState* old_state = run_state_; + RunState run_state; + run_state_ = &run_state; + for (;;) { + bool did_work = DoDelayedWork(); + if (run_state.should_quit) + break; + did_work |= Wait(run_mode == UNTIL_IDLE); + if (run_state.should_quit) + break; + if (!did_work && run_mode == UNTIL_IDLE) + break; + } + run_state_ = old_state; +} + +bool RunLoop::DoDelayedWork() { + MojoTimeTicks now = GetTimeTicksNow(); + if (!delayed_tasks_.empty() && delayed_tasks_.top().run_time <= now) { + PendingTask task = delayed_tasks_.top(); + delayed_tasks_.pop(); + task.task.Run(); + return true; + } + return false; +} + +void RunLoop::Quit() { + assert(current() == this); + if (run_state_) + run_state_->should_quit = true; +} + +void RunLoop::PostDelayedTask(const Closure& task, MojoTimeTicks delay) { + assert(current() == this); + MojoTimeTicks run_time = delay + GetTimeTicksNow(); + delayed_tasks_.push(PendingTask(task, run_time, next_sequence_number_++)); +} + +bool RunLoop::Wait(bool non_blocking) { + const WaitState wait_state = GetWaitState(non_blocking); + if (wait_state.handles.empty()) { + if (delayed_tasks_.empty()) + Quit(); + return false; + } + + const WaitManyResult wmr = + WaitMany(wait_state.handles, wait_state.handle_signals, + wait_state.deadline, nullptr); + + if (!wmr.IsIndexValid()) { + assert(wmr.result == MOJO_RESULT_DEADLINE_EXCEEDED); + return NotifyHandlers(MOJO_RESULT_DEADLINE_EXCEEDED, CHECK_DEADLINE); + } + + Handle handle = wait_state.handles[wmr.index]; + assert(handler_data_.find(handle) != handler_data_.end()); + RunLoopHandler* handler = handler_data_[handle].handler; + + switch (wmr.result) { + case MOJO_RESULT_OK: + handler->OnHandleReady(handle); + return true; + case MOJO_RESULT_INVALID_ARGUMENT: + case MOJO_RESULT_FAILED_PRECONDITION: + // Remove the handle first, this way if OnHandleError() tries to remove + // the handle our iterator isn't invalidated. + handler_data_.erase(handle); + handler->OnHandleError(handle, wmr.result); + return true; + default: + assert(false); + return false; + } +} + +bool RunLoop::NotifyHandlers(MojoResult error, CheckDeadline check) { + bool notified = false; + + // Make a copy in case someone tries to add/remove new handlers as part of + // notifying. + const HandleToHandlerData cloned_handlers(handler_data_); + const MojoTimeTicks now(GetTimeTicksNow()); + for (HandleToHandlerData::const_iterator i = cloned_handlers.begin(); + i != cloned_handlers.end(); + ++i) { + // Only check deadline exceeded if that's what we're notifying. + if (check == CHECK_DEADLINE && + (i->second.deadline == kInvalidTimeTicks || i->second.deadline > now)) { + continue; + } + + // Since we're iterating over a clone of the handlers, verify the handler + // is still valid before notifying. + if (handler_data_.find(i->first) == handler_data_.end() || + handler_data_[i->first].id != i->second.id) { + continue; + } + + RunLoopHandler* handler = i->second.handler; + handler_data_.erase(i->first); + handler->OnHandleError(i->first, error); + notified = true; + } + + return notified; +} + +RunLoop::WaitState RunLoop::GetWaitState(bool non_blocking) const { + WaitState wait_state; + MojoTimeTicks min_time = kInvalidTimeTicks; + for (HandleToHandlerData::const_iterator i = handler_data_.begin(); + i != handler_data_.end(); + ++i) { + wait_state.handles.push_back(i->first); + wait_state.handle_signals.push_back(i->second.handle_signals); + if (!non_blocking && i->second.deadline != kInvalidTimeTicks && + (min_time == kInvalidTimeTicks || i->second.deadline < min_time)) { + min_time = i->second.deadline; + } + } + if (!delayed_tasks_.empty()) { + MojoTimeTicks delayed_min_time = delayed_tasks_.top().run_time; + if (min_time == kInvalidTimeTicks) + min_time = delayed_min_time; + else + min_time = std::min(min_time, delayed_min_time); + } + if (non_blocking) { + wait_state.deadline = static_cast<MojoDeadline>(0); + } else if (min_time != kInvalidTimeTicks) { + const MojoTimeTicks now = GetTimeTicksNow(); + if (min_time < now) + wait_state.deadline = static_cast<MojoDeadline>(0); + else + wait_state.deadline = static_cast<MojoDeadline>(min_time - now); + } + return wait_state; +} + +RunLoop::PendingTask::PendingTask(const Closure& task, + MojoTimeTicks run_time, + uint64_t sequence_number) + : task(task), run_time(run_time), sequence_number(sequence_number) { +} + +RunLoop::PendingTask::~PendingTask() { +} + +bool RunLoop::PendingTask::operator<(const RunLoop::PendingTask& other) const { + if (run_time != other.run_time) { + // std::priority_queue<> puts the least element at the end of the queue. We + // want the soonest eligible task to be at the head of the queue, so + // run_times further in the future are considered lesser. + return run_time > other.run_time; + } + + return sequence_number > other.sequence_number; +} + +} // namespace mojo diff --git a/mojo/public/cpp/utility/lib/thread.cc b/mojo/public/cpp/utility/lib/thread.cc new file mode 100644 index 0000000..40f0bdd --- /dev/null +++ b/mojo/public/cpp/utility/lib/thread.cc @@ -0,0 +1,64 @@ +// 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. + +#include "mojo/public/cpp/utility/thread.h" + +#include <assert.h> + +namespace mojo { + +Thread::Thread() : options_(), thread_(), started_(false), joined_(false) { +} + +Thread::Thread(const Options& options) + : options_(options), thread_(), started_(false), joined_(false) { +} + +Thread::~Thread() { + // If it was started, it must have been joined. + assert(!started_ || joined_); +} + +void Thread::Start() { + assert(!started_); + assert(!joined_); + + pthread_attr_t attr; + int rv = pthread_attr_init(&attr); + MOJO_ALLOW_UNUSED_LOCAL(rv); + assert(rv == 0); + + // Non-default stack size? + if (options_.stack_size() != 0) { + rv = pthread_attr_setstacksize(&attr, options_.stack_size()); + assert(rv == 0); + } + + started_ = true; + rv = pthread_create(&thread_, &attr, &ThreadRunTrampoline, this); + assert(rv == 0); + + rv = pthread_attr_destroy(&attr); + assert(rv == 0); +} + +void Thread::Join() { + // Must have been started but not yet joined. + assert(started_); + assert(!joined_); + + joined_ = true; + int rv = pthread_join(thread_, nullptr); + MOJO_ALLOW_UNUSED_LOCAL(rv); + assert(rv == 0); +} + +// static +void* Thread::ThreadRunTrampoline(void* arg) { + Thread* self = static_cast<Thread*>(arg); + self->Run(); + return nullptr; +} + +} // namespace mojo diff --git a/mojo/public/cpp/utility/lib/thread_local.h b/mojo/public/cpp/utility/lib/thread_local.h new file mode 100644 index 0000000..f5461ee --- /dev/null +++ b/mojo/public/cpp/utility/lib/thread_local.h @@ -0,0 +1,54 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_UTILITY_LIB_THREAD_LOCAL_H_ +#define MOJO_PUBLIC_CPP_UTILITY_LIB_THREAD_LOCAL_H_ + +#ifndef _WIN32 +#include <pthread.h> +#endif + +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { +namespace internal { + +// Helper functions that abstract the cross-platform APIs. +struct ThreadLocalPlatform { +#ifdef _WIN32 + typedef unsigned long SlotType; +#else + typedef pthread_key_t SlotType; +#endif + + static void AllocateSlot(SlotType* slot); + static void FreeSlot(SlotType slot); + static void* GetValueFromSlot(SlotType slot); + static void SetValueInSlot(SlotType slot, void* value); +}; + +// This class is intended to be statically allocated. +template <typename P> +class ThreadLocalPointer { + public: + ThreadLocalPointer() : slot_() {} + + void Allocate() { ThreadLocalPlatform::AllocateSlot(&slot_); } + + void Free() { ThreadLocalPlatform::FreeSlot(slot_); } + + P* Get() { + return static_cast<P*>(ThreadLocalPlatform::GetValueFromSlot(slot_)); + } + + void Set(P* value) { ThreadLocalPlatform::SetValueInSlot(slot_, value); } + + private: + ThreadLocalPlatform::SlotType slot_; +}; + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_UTILITY_LIB_THREAD_LOCAL_H_ diff --git a/mojo/public/cpp/utility/lib/thread_local_posix.cc b/mojo/public/cpp/utility/lib/thread_local_posix.cc new file mode 100644 index 0000000..ea7343e --- /dev/null +++ b/mojo/public/cpp/utility/lib/thread_local_posix.cc @@ -0,0 +1,39 @@ +// 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. + +#include "mojo/public/cpp/utility/lib/thread_local.h" + +#include <assert.h> + +namespace mojo { +namespace internal { + +// static +void ThreadLocalPlatform::AllocateSlot(SlotType* slot) { + if (pthread_key_create(slot, nullptr) != 0) { + assert(false); + } +} + +// static +void ThreadLocalPlatform::FreeSlot(SlotType slot) { + if (pthread_key_delete(slot) != 0) { + assert(false); + } +} + +// static +void* ThreadLocalPlatform::GetValueFromSlot(SlotType slot) { + return pthread_getspecific(slot); +} + +// static +void ThreadLocalPlatform::SetValueInSlot(SlotType slot, void* value) { + if (pthread_setspecific(slot, value) != 0) { + assert(false); + } +} + +} // namespace internal +} // namespace mojo diff --git a/mojo/public/cpp/utility/lib/thread_local_win.cc b/mojo/public/cpp/utility/lib/thread_local_win.cc new file mode 100644 index 0000000..b8239cb --- /dev/null +++ b/mojo/public/cpp/utility/lib/thread_local_win.cc @@ -0,0 +1,39 @@ +// 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. + +#include "mojo/public/cpp/utility/lib/thread_local.h" + +#include <windows.h> +#include <assert.h> + +namespace mojo { +namespace internal { + +// static +void ThreadLocalPlatform::AllocateSlot(SlotType* slot) { + *slot = TlsAlloc(); + assert(*slot != TLS_OUT_OF_INDEXES); +} + +// static +void ThreadLocalPlatform::FreeSlot(SlotType slot) { + if (!TlsFree(slot)) { + assert(false); + } +} + +// static +void* ThreadLocalPlatform::GetValueFromSlot(SlotType slot) { + return TlsGetValue(slot); +} + +// static +void ThreadLocalPlatform::SetValueInSlot(SlotType slot, void* value) { + if (!TlsSetValue(slot, value)) { + assert(false); + } +} + +} // namespace internal +} // namespace mojo diff --git a/mojo/public/cpp/utility/mutex.h b/mojo/public/cpp/utility/mutex.h new file mode 100644 index 0000000..4dc4aee --- /dev/null +++ b/mojo/public/cpp/utility/mutex.h @@ -0,0 +1,70 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_UTILITY_MUTEX_H_ +#define MOJO_PUBLIC_CPP_UTILITY_MUTEX_H_ + +#ifdef _WIN32 +#error "Not implemented: See crbug.com/342893." +#endif + +#include <pthread.h> + +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { + +#ifdef NDEBUG +// Note: Make a C++ constant for |PTHREAD_MUTEX_INITIALIZER|. (We can't directly +// use the C macro in an initializer list, since it might expand to |{ ... }|.) +namespace internal { +const pthread_mutex_t kPthreadMutexInitializer = PTHREAD_MUTEX_INITIALIZER; +} +#endif + +class Mutex { + public: +#ifdef NDEBUG + Mutex() : mutex_(internal::kPthreadMutexInitializer) {} + ~Mutex() { pthread_mutex_destroy(&mutex_); } + + void Lock() { pthread_mutex_lock(&mutex_); } + void Unlock() { pthread_mutex_unlock(&mutex_); } + bool TryLock() { return pthread_mutex_trylock(&mutex_) == 0; } + + void AssertHeld() {} +#else + Mutex(); + ~Mutex(); + + void Lock(); + void Unlock(); + bool TryLock(); + + void AssertHeld(); +#endif + + private: + pthread_mutex_t mutex_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(Mutex); +}; + +class MutexLock { + public: + explicit MutexLock(Mutex* mutex) : mutex_(mutex) { mutex_->Lock(); } + ~MutexLock() { mutex_->Unlock(); } + + private: + Mutex* const mutex_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(MutexLock); +}; + +// Catch bug where variable name is omitted (e.g., |MutexLock (&mu)|). +#define MutexLock(x) static_assert(0, "MutexLock() missing variable name"); + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_UTILITY_MUTEX_H_ diff --git a/mojo/public/cpp/utility/run_loop.h b/mojo/public/cpp/utility/run_loop.h new file mode 100644 index 0000000..4673eaa --- /dev/null +++ b/mojo/public/cpp/utility/run_loop.h @@ -0,0 +1,156 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_UTILITY_RUN_LOOP_H_ +#define MOJO_PUBLIC_CPP_UTILITY_RUN_LOOP_H_ + +#include <map> +#include <queue> + +#include "mojo/public/cpp/bindings/callback.h" +#include "mojo/public/cpp/system/core.h" + +namespace mojo { + +class RunLoopHandler; + +// Watches handles for signals and calls event handlers when they occur. Also +// executes delayed tasks. This class should only be used by a single thread. +class RunLoop { + public: + RunLoop(); + ~RunLoop(); + + // Sets up state needed for RunLoop. This must be invoked before creating a + // RunLoop. + static void SetUp(); + + // Cleans state created by Setup(). + static void TearDown(); + + // Returns the RunLoop for the current thread. Returns null if not yet + // created. + static RunLoop* current(); + + // Registers a RunLoopHandler for the specified handle. It is an error to + // register more than one handler for a handle, and crashes the process. + // + // The handler's OnHandleReady() method is invoked after one of the signals in + // |handle_signals| occurs. Note that the handler remains registered until + // explicitly removed or an error occurs. + // + // The handler's OnHandleError() method is invoked if the deadline elapses, an + // error is detected, or the RunLoop is being destroyed. The handler is + // automatically unregistered before calling OnHandleError(), so it will not + // receive any further notifications. + void AddHandler(RunLoopHandler* handler, + const Handle& handle, + MojoHandleSignals handle_signals, + MojoDeadline deadline); + void RemoveHandler(const Handle& handle); + bool HasHandler(const Handle& handle) const; + + // Runs the loop servicing handles and tasks as they are ready. This returns + // when Quit() is invoked, or there are no more handles nor tasks. + void Run(); + + // Runs the loop servicing any handles and tasks that are ready. Does not wait + // for handles or tasks to become ready before returning. Returns early if + // Quit() is invoked. + void RunUntilIdle(); + + void Quit(); + + // Adds a task to be performed after delay has elapsed. Must be posted to the + // current thread's RunLoop. + void PostDelayedTask(const Closure& task, MojoTimeTicks delay); + + private: + struct RunState; + struct WaitState; + + // Contains the data needed to track a request to AddHandler(). + struct HandlerData { + HandlerData() + : handler(nullptr), + handle_signals(MOJO_HANDLE_SIGNAL_NONE), + deadline(0), + id(0) {} + + RunLoopHandler* handler; + MojoHandleSignals handle_signals; + MojoTimeTicks deadline; + // See description of |RunLoop::next_handler_id_| for details. + int id; + }; + + typedef std::map<Handle, HandlerData> HandleToHandlerData; + + // Used for NotifyHandlers to specify whether HandlerData's |deadline| + // should be checked prior to notifying. + enum CheckDeadline { CHECK_DEADLINE, IGNORE_DEADLINE }; + + // Mode of operation of the run loop. + enum RunMode { UNTIL_EMPTY, UNTIL_IDLE }; + + // Runs the loop servicing any handles and tasks that are ready. If + // |run_mode| is |UNTIL_IDLE|, does not wait for handles or tasks to become + // ready before returning. Returns early if Quit() is invoked. + void RunInternal(RunMode run_mode); + + // Do one unit of delayed work, if eligible. Returns true is a task was run. + bool DoDelayedWork(); + + // Waits for a handle to be ready or until the next task must be run. Returns + // after servicing at least one handle (or there are no more handles) unless + // a task must be run or |non_blocking| is true, in which case it will also + // return if no task is registered and servicing at least one handle would + // require blocking. Returns true if a RunLoopHandler was notified. + bool Wait(bool non_blocking); + + // Notifies handlers of |error|. If |check| == CHECK_DEADLINE, this will + // only notify handlers whose deadline has expired and skips the rest. + // Returns true if a RunLoopHandler was notified. + bool NotifyHandlers(MojoResult error, CheckDeadline check); + + // Returns the state needed to pass to WaitMany(). + WaitState GetWaitState(bool non_blocking) const; + + HandleToHandlerData handler_data_; + + // If non-null we're running (inside Run()). Member references a value on the + // stack. + RunState* run_state_; + + // An ever increasing value assigned to each HandlerData::id. Used to detect + // uniqueness while notifying. That is, while notifying expired timers we copy + // |handler_data_| and only notify handlers whose id match. If the id does not + // match it means the handler was removed then added so that we shouldn't + // notify it. + int next_handler_id_; + + struct PendingTask { + PendingTask(const Closure& task, + MojoTimeTicks runtime, + uint64_t sequence_number); + ~PendingTask(); + + bool operator<(const PendingTask& other) const; + + Closure task; + MojoTimeTicks run_time; + uint64_t sequence_number; + }; + // An ever increasing sequence number attached to each pending task in order + // to preserve relative order of tasks posted at the 'same' time. + uint64_t next_sequence_number_; + typedef std::priority_queue<PendingTask> DelayedTaskQueue; + DelayedTaskQueue delayed_tasks_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(RunLoop); +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_UTILITY_RUN_LOOP_H_ diff --git a/mojo/public/cpp/utility/run_loop_handler.h b/mojo/public/cpp/utility/run_loop_handler.h new file mode 100644 index 0000000..69838d5 --- /dev/null +++ b/mojo/public/cpp/utility/run_loop_handler.h @@ -0,0 +1,25 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_UTILITY_RUN_LOOP_HANDLER_H_ +#define MOJO_PUBLIC_CPP_UTILITY_RUN_LOOP_HANDLER_H_ + +#include "mojo/public/cpp/system/core.h" + +namespace mojo { + +// Used by RunLoop to notify when a handle is either ready or has become +// invalid. +class RunLoopHandler { + public: + virtual void OnHandleReady(const Handle& handle) = 0; + virtual void OnHandleError(const Handle& handle, MojoResult result) = 0; + + protected: + virtual ~RunLoopHandler() {} +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_UTILITY_RUN_LOOP_HANDLER_H_ diff --git a/mojo/public/cpp/utility/tests/BUILD.gn b/mojo/public/cpp/utility/tests/BUILD.gn new file mode 100644 index 0000000..acbbc9f --- /dev/null +++ b/mojo/public/cpp/utility/tests/BUILD.gn @@ -0,0 +1,32 @@ +# 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. + +import("../../../mojo_sdk.gni") + +mojo_sdk_source_set("tests") { + testonly = true + + sources = [ + "run_loop_unittest.cc", + ] + + deps = [ + "//testing/gtest", + ] + + mojo_sdk_deps = [ + "mojo/public/cpp/environment:standalone", + "mojo/public/cpp/system", + "mojo/public/cpp/test_support:test_utils", + "mojo/public/cpp/utility", + ] + + # crbug.com/342893 + if (!is_win) { + sources += [ + "mutex_unittest.cc", + "thread_unittest.cc", + ] + } +} diff --git a/mojo/public/cpp/utility/tests/mutex_unittest.cc b/mojo/public/cpp/utility/tests/mutex_unittest.cc new file mode 100644 index 0000000..78e95c5 --- /dev/null +++ b/mojo/public/cpp/utility/tests/mutex_unittest.cc @@ -0,0 +1,259 @@ +// 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. + +#include "mojo/public/cpp/utility/mutex.h" + +#include <stdlib.h> // For |rand()|. +#include <time.h> // For |nanosleep()| (defined by POSIX). + +#include <vector> + +#include "mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/utility/thread.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace { + +TEST(MutexTest, TrivialSingleThreaded) { + Mutex mutex; + + mutex.Lock(); + mutex.AssertHeld(); + mutex.Unlock(); + + EXPECT_TRUE(mutex.TryLock()); + mutex.AssertHeld(); + mutex.Unlock(); + + { + MutexLock lock(&mutex); + mutex.AssertHeld(); + } + + EXPECT_TRUE(mutex.TryLock()); + mutex.Unlock(); +} + +class Fiddler { + public: + enum Type { kTypeLock, kTypeTry }; + Fiddler(size_t times_to_lock, + Type type, + bool should_sleep, + Mutex* mutex, + int* shared_value) + : times_to_lock_(times_to_lock), + type_(type), + should_sleep_(should_sleep), + mutex_(mutex), + shared_value_(shared_value) { + } + + ~Fiddler() { + } + + void Fiddle() { + for (size_t i = 0; i < times_to_lock_;) { + switch (type_) { + case kTypeLock: { + mutex_->Lock(); + int old_shared_value = *shared_value_; + if (should_sleep_) + SleepALittle(); + *shared_value_ = old_shared_value + 1; + mutex_->Unlock(); + i++; + break; + } + case kTypeTry: + if (mutex_->TryLock()) { + int old_shared_value = *shared_value_; + if (should_sleep_) + SleepALittle(); + *shared_value_ = old_shared_value + 1; + mutex_->Unlock(); + i++; + } else { + SleepALittle(); // Don't spin. + } + break; + } + } + } + + private: + static void SleepALittle() { + static const long kNanosPerMilli = 1000000; + struct timespec req = { + 0, // Seconds. + (rand() % 10) * kNanosPerMilli // Nanoseconds. + }; + int rv = nanosleep(&req, nullptr); + MOJO_ALLOW_UNUSED_LOCAL(rv); + assert(rv == 0); + } + + const size_t times_to_lock_; + const Type type_; + const bool should_sleep_; + Mutex* const mutex_; + int* const shared_value_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(Fiddler); +}; + +class FiddlerThread : public Thread { + public: + // Takes ownership of |fiddler|. + FiddlerThread(Fiddler* fiddler) + : fiddler_(fiddler) { + } + + ~FiddlerThread() override { delete fiddler_; } + + void Run() override { fiddler_->Fiddle(); } + + private: + Fiddler* const fiddler_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(FiddlerThread); +}; + +// This does a stress test (that also checks exclusion). +TEST(MutexTest, ThreadedStress) { + static const size_t kNumThreads = 20; + static const int kTimesToLockEach = 20; + assert(kNumThreads % 4 == 0); + + Mutex mutex; + int shared_value = 0; + + std::vector<FiddlerThread*> fiddler_threads; + + for (size_t i = 0; i < kNumThreads; i += 4) { + fiddler_threads.push_back(new FiddlerThread(new Fiddler( + kTimesToLockEach, Fiddler::kTypeLock, false, &mutex, &shared_value))); + fiddler_threads.push_back(new FiddlerThread(new Fiddler( + kTimesToLockEach, Fiddler::kTypeTry, false, &mutex, &shared_value))); + fiddler_threads.push_back(new FiddlerThread(new Fiddler( + kTimesToLockEach, Fiddler::kTypeLock, true, &mutex, &shared_value))); + fiddler_threads.push_back(new FiddlerThread(new Fiddler( + kTimesToLockEach, Fiddler::kTypeTry, true, &mutex, &shared_value))); + } + + for (size_t i = 0; i < kNumThreads; i++) + fiddler_threads[i]->Start(); + + // Do some fiddling ourselves. + Fiddler(kTimesToLockEach, Fiddler::kTypeLock, true, &mutex, &shared_value) + .Fiddle(); + + // Join. + for (size_t i = 0; i < kNumThreads; i++) + fiddler_threads[i]->Join(); + + EXPECT_EQ(static_cast<int>(kNumThreads + 1) * kTimesToLockEach, shared_value); + + // Delete. + for (size_t i = 0; i < kNumThreads; i++) + delete fiddler_threads[i]; + fiddler_threads.clear(); +} + +class TryThread : public Thread { + public: + explicit TryThread(Mutex* mutex) : mutex_(mutex), try_lock_succeeded_() {} + ~TryThread() override {} + + void Run() override { + try_lock_succeeded_ = mutex_->TryLock(); + if (try_lock_succeeded_) + mutex_->Unlock(); + } + + bool try_lock_succeeded() const { return try_lock_succeeded_; } + + private: + Mutex* const mutex_; + bool try_lock_succeeded_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(TryThread); +}; + +TEST(MutexTest, TryLock) { + Mutex mutex; + + // |TryLock()| should succeed -- we don't have the lock. + { + TryThread thread(&mutex); + thread.Start(); + thread.Join(); + EXPECT_TRUE(thread.try_lock_succeeded()); + } + + // Take the lock. + ASSERT_TRUE(mutex.TryLock()); + + // Now it should fail. + { + TryThread thread(&mutex); + thread.Start(); + thread.Join(); + EXPECT_FALSE(thread.try_lock_succeeded()); + } + + // Release the lock. + mutex.Unlock(); + + // It should succeed again. + { + TryThread thread(&mutex); + thread.Start(); + thread.Join(); + EXPECT_TRUE(thread.try_lock_succeeded()); + } +} + + +// Tests of assertions for Debug builds. +#if !defined(NDEBUG) +// Test |AssertHeld()| (which is an actual user API). +TEST(MutexTest, DebugAssertHeldFailure) { + Mutex mutex; + EXPECT_DEATH_IF_SUPPORTED(mutex.AssertHeld(), ""); +} + +// Test other consistency checks. +TEST(MutexTest, DebugAssertionFailures) { + // Unlock without lock held. + EXPECT_DEATH_IF_SUPPORTED({ + Mutex mutex; + mutex.Unlock(); + }, ""); + + // Lock with lock held (on same thread). + EXPECT_DEATH_IF_SUPPORTED({ + Mutex mutex; + mutex.Lock(); + mutex.Lock(); + }, ""); + + // Try lock with lock held. + EXPECT_DEATH_IF_SUPPORTED({ + Mutex mutex; + mutex.Lock(); + mutex.TryLock(); + }, ""); + + // Destroy lock with lock held. + EXPECT_DEATH_IF_SUPPORTED({ + Mutex mutex; + mutex.Lock(); + }, ""); +} +#endif // !defined(NDEBUG) + +} // namespace +} // namespace mojo diff --git a/mojo/public/cpp/utility/tests/run_loop_unittest.cc b/mojo/public/cpp/utility/tests/run_loop_unittest.cc new file mode 100644 index 0000000..4ab4876 --- /dev/null +++ b/mojo/public/cpp/utility/tests/run_loop_unittest.cc @@ -0,0 +1,425 @@ +// 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. + +#include "mojo/public/cpp/utility/run_loop.h" + +#include <string> + +#include "mojo/public/cpp/system/core.h" +#include "mojo/public/cpp/test_support/test_utils.h" +#include "mojo/public/cpp/utility/run_loop_handler.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace { + +class TestRunLoopHandler : public RunLoopHandler { + public: + TestRunLoopHandler() + : ready_count_(0), + error_count_(0), + last_error_result_(MOJO_RESULT_OK) { + } + ~TestRunLoopHandler() override {} + + void clear_ready_count() { ready_count_ = 0; } + int ready_count() const { return ready_count_; } + + void clear_error_count() { error_count_ = 0; } + int error_count() const { return error_count_; } + + MojoResult last_error_result() const { return last_error_result_; } + + // RunLoopHandler: + void OnHandleReady(const Handle& handle) override { ready_count_++; } + void OnHandleError(const Handle& handle, MojoResult result) override { + error_count_++; + last_error_result_ = result; + } + + private: + int ready_count_; + int error_count_; + MojoResult last_error_result_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(TestRunLoopHandler); +}; + +class RunLoopTest : public testing::Test { + public: + RunLoopTest() {} + + void SetUp() override { + Test::SetUp(); + RunLoop::SetUp(); + } + void TearDown() override { + RunLoop::TearDown(); + Test::TearDown(); + } + + private: + MOJO_DISALLOW_COPY_AND_ASSIGN(RunLoopTest); +}; + +// Trivial test to verify Run() with no added handles returns. +TEST_F(RunLoopTest, ExitsWithNoHandles) { + RunLoop run_loop; + run_loop.Run(); +} + +class RemoveOnReadyRunLoopHandler : public TestRunLoopHandler { + public: + RemoveOnReadyRunLoopHandler() : run_loop_(nullptr) {} + ~RemoveOnReadyRunLoopHandler() override {} + + void set_run_loop(RunLoop* run_loop) { run_loop_ = run_loop; } + + // RunLoopHandler: + void OnHandleReady(const Handle& handle) override { + run_loop_->RemoveHandler(handle); + TestRunLoopHandler::OnHandleReady(handle); + } + + private: + RunLoop* run_loop_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(RemoveOnReadyRunLoopHandler); +}; + +// Verifies RunLoop quits when no more handles (handle is removed when ready). +TEST_F(RunLoopTest, HandleReady) { + RemoveOnReadyRunLoopHandler handler; + MessagePipe test_pipe; + EXPECT_TRUE(test::WriteTextMessage(test_pipe.handle1.get(), std::string())); + + RunLoop run_loop; + handler.set_run_loop(&run_loop); + run_loop.AddHandler(&handler, test_pipe.handle0.get(), + MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE); + run_loop.Run(); + EXPECT_EQ(1, handler.ready_count()); + EXPECT_EQ(0, handler.error_count()); + EXPECT_FALSE(run_loop.HasHandler(test_pipe.handle0.get())); +} + +class QuitOnReadyRunLoopHandler : public TestRunLoopHandler { + public: + QuitOnReadyRunLoopHandler() : run_loop_(nullptr) {} + ~QuitOnReadyRunLoopHandler() override {} + + void set_run_loop(RunLoop* run_loop) { run_loop_ = run_loop; } + + // RunLoopHandler: + void OnHandleReady(const Handle& handle) override { + run_loop_->Quit(); + TestRunLoopHandler::OnHandleReady(handle); + } + + private: + RunLoop* run_loop_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(QuitOnReadyRunLoopHandler); +}; + +// Verifies Quit() from OnHandleReady() quits the loop. +TEST_F(RunLoopTest, QuitFromReady) { + QuitOnReadyRunLoopHandler handler; + MessagePipe test_pipe; + EXPECT_TRUE(test::WriteTextMessage(test_pipe.handle1.get(), std::string())); + + RunLoop run_loop; + handler.set_run_loop(&run_loop); + run_loop.AddHandler(&handler, test_pipe.handle0.get(), + MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE); + run_loop.Run(); + EXPECT_EQ(1, handler.ready_count()); + EXPECT_EQ(0, handler.error_count()); + EXPECT_TRUE(run_loop.HasHandler(test_pipe.handle0.get())); +} + +class QuitOnErrorRunLoopHandler : public TestRunLoopHandler { + public: + QuitOnErrorRunLoopHandler() : run_loop_(nullptr) {} + ~QuitOnErrorRunLoopHandler() override {} + + void set_run_loop(RunLoop* run_loop) { run_loop_ = run_loop; } + + // RunLoopHandler: + void OnHandleError(const Handle& handle, MojoResult result) override { + run_loop_->Quit(); + TestRunLoopHandler::OnHandleError(handle, result); + } + + private: + RunLoop* run_loop_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(QuitOnErrorRunLoopHandler); +}; + +// Verifies Quit() when the deadline is reached works. +TEST_F(RunLoopTest, QuitWhenDeadlineExpired) { + QuitOnErrorRunLoopHandler handler; + MessagePipe test_pipe; + RunLoop run_loop; + handler.set_run_loop(&run_loop); + run_loop.AddHandler(&handler, test_pipe.handle0.get(), + MOJO_HANDLE_SIGNAL_READABLE, + static_cast<MojoDeadline>(10000)); + run_loop.Run(); + EXPECT_EQ(0, handler.ready_count()); + EXPECT_EQ(1, handler.error_count()); + EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, handler.last_error_result()); + EXPECT_FALSE(run_loop.HasHandler(test_pipe.handle0.get())); +} + +// Test that handlers are notified of loop destruction. +TEST_F(RunLoopTest, Destruction) { + TestRunLoopHandler handler; + MessagePipe test_pipe; + { + RunLoop run_loop; + run_loop.AddHandler(&handler, + test_pipe.handle0.get(), + MOJO_HANDLE_SIGNAL_READABLE, + MOJO_DEADLINE_INDEFINITE); + } + EXPECT_EQ(1, handler.error_count()); + EXPECT_EQ(MOJO_RESULT_ABORTED, handler.last_error_result()); +} + +class RemoveManyRunLoopHandler : public TestRunLoopHandler { + public: + RemoveManyRunLoopHandler() : run_loop_(nullptr) {} + ~RemoveManyRunLoopHandler() override {} + + void set_run_loop(RunLoop* run_loop) { run_loop_ = run_loop; } + void add_handle(const Handle& handle) { handles_.push_back(handle); } + + // RunLoopHandler: + void OnHandleError(const Handle& handle, MojoResult result) override { + for (size_t i = 0; i < handles_.size(); i++) + run_loop_->RemoveHandler(handles_[i]); + TestRunLoopHandler::OnHandleError(handle, result); + } + + private: + std::vector<Handle> handles_; + RunLoop* run_loop_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(RemoveManyRunLoopHandler); +}; + +// Test that handlers are notified of loop destruction. +TEST_F(RunLoopTest, MultipleHandleDestruction) { + RemoveManyRunLoopHandler odd_handler; + TestRunLoopHandler even_handler; + MessagePipe test_pipe1, test_pipe2, test_pipe3; + { + RunLoop run_loop; + odd_handler.set_run_loop(&run_loop); + odd_handler.add_handle(test_pipe1.handle0.get()); + odd_handler.add_handle(test_pipe3.handle0.get()); + run_loop.AddHandler(&odd_handler, + test_pipe1.handle0.get(), + MOJO_HANDLE_SIGNAL_READABLE, + MOJO_DEADLINE_INDEFINITE); + run_loop.AddHandler(&even_handler, + test_pipe2.handle0.get(), + MOJO_HANDLE_SIGNAL_READABLE, + MOJO_DEADLINE_INDEFINITE); + run_loop.AddHandler(&odd_handler, + test_pipe3.handle0.get(), + MOJO_HANDLE_SIGNAL_READABLE, + MOJO_DEADLINE_INDEFINITE); + } + EXPECT_EQ(1, odd_handler.error_count()); + EXPECT_EQ(1, even_handler.error_count()); + EXPECT_EQ(MOJO_RESULT_ABORTED, odd_handler.last_error_result()); + EXPECT_EQ(MOJO_RESULT_ABORTED, even_handler.last_error_result()); +} + +class AddHandlerOnErrorHandler : public TestRunLoopHandler { + public: + AddHandlerOnErrorHandler() : run_loop_(nullptr) {} + ~AddHandlerOnErrorHandler() override {} + + void set_run_loop(RunLoop* run_loop) { run_loop_ = run_loop; } + + // RunLoopHandler: + void OnHandleError(const Handle& handle, MojoResult result) override { + run_loop_->AddHandler(this, handle, + MOJO_HANDLE_SIGNAL_READABLE, + MOJO_DEADLINE_INDEFINITE); + TestRunLoopHandler::OnHandleError(handle, result); + } + + private: + RunLoop* run_loop_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(AddHandlerOnErrorHandler); +}; + +TEST_F(RunLoopTest, AddHandlerOnError) { + AddHandlerOnErrorHandler handler; + MessagePipe test_pipe; + { + RunLoop run_loop; + handler.set_run_loop(&run_loop); + run_loop.AddHandler(&handler, + test_pipe.handle0.get(), + MOJO_HANDLE_SIGNAL_READABLE, + MOJO_DEADLINE_INDEFINITE); + } + EXPECT_EQ(1, handler.error_count()); + EXPECT_EQ(MOJO_RESULT_ABORTED, handler.last_error_result()); +} + +TEST_F(RunLoopTest, Current) { + EXPECT_TRUE(RunLoop::current() == nullptr); + { + RunLoop run_loop; + EXPECT_EQ(&run_loop, RunLoop::current()); + } + EXPECT_TRUE(RunLoop::current() == nullptr); +} + +class NestingRunLoopHandler : public TestRunLoopHandler { + public: + static const size_t kDepthLimit; + static const char kSignalMagic; + + NestingRunLoopHandler() + : run_loop_(nullptr), + pipe_(nullptr), + depth_(0), + reached_depth_limit_(false) {} + + ~NestingRunLoopHandler() override {} + + void set_run_loop(RunLoop* run_loop) { run_loop_ = run_loop; } + void set_pipe(MessagePipe* pipe) { pipe_ = pipe; } + bool reached_depth_limit() const { return reached_depth_limit_; } + + // RunLoopHandler: + void OnHandleReady(const Handle& handle) override { + TestRunLoopHandler::OnHandleReady(handle); + EXPECT_EQ(handle.value(), pipe_->handle0.get().value()); + + ReadSignal(); + size_t current_depth = ++depth_; + if (current_depth < kDepthLimit) { + WriteSignal(); + run_loop_->Run(); + if (current_depth == kDepthLimit - 1) { + // The topmost loop Quit()-ed, so its parent takes back the + // control without exeeding deadline. + EXPECT_EQ(error_count(), 0); + } else { + EXPECT_EQ(error_count(), 1); + } + + } else { + EXPECT_EQ(current_depth, kDepthLimit); + reached_depth_limit_ = true; + run_loop_->Quit(); + } + --depth_; + } + + void WriteSignal() { + char write_byte = kSignalMagic; + MojoResult write_result = + WriteMessageRaw(pipe_->handle1.get(), &write_byte, 1, nullptr, 0, + MOJO_WRITE_MESSAGE_FLAG_NONE); + EXPECT_EQ(write_result, MOJO_RESULT_OK); + } + + void ReadSignal() { + char read_byte = 0; + uint32_t bytes_read = 1; + uint32_t handles_read = 0; + MojoResult read_result = + ReadMessageRaw(pipe_->handle0.get(), &read_byte, &bytes_read, nullptr, + &handles_read, MOJO_READ_MESSAGE_FLAG_NONE); + EXPECT_EQ(read_result, MOJO_RESULT_OK); + EXPECT_EQ(read_byte, kSignalMagic); + } + + private: + RunLoop* run_loop_; + MessagePipe* pipe_; + size_t depth_; + bool reached_depth_limit_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(NestingRunLoopHandler); +}; + +const size_t NestingRunLoopHandler::kDepthLimit = 10; +const char NestingRunLoopHandler::kSignalMagic = 'X'; + +TEST_F(RunLoopTest, NestedRun) { + NestingRunLoopHandler handler; + MessagePipe test_pipe; + RunLoop run_loop; + handler.set_run_loop(&run_loop); + handler.set_pipe(&test_pipe); + run_loop.AddHandler(&handler, test_pipe.handle0.get(), + MOJO_HANDLE_SIGNAL_READABLE, + static_cast<MojoDeadline>(10000)); + handler.WriteSignal(); + run_loop.Run(); + + EXPECT_TRUE(handler.reached_depth_limit()); + // Got MOJO_RESULT_DEADLINE_EXCEEDED once then removed from the + // RunLoop's handler list. + EXPECT_EQ(handler.error_count(), 1); + EXPECT_EQ(handler.last_error_result(), MOJO_RESULT_DEADLINE_EXCEEDED); +} + +struct Task { + Task(int num, std::vector<int>* sequence) : num(num), sequence(sequence) {} + + void Run() const { sequence->push_back(num); } + + int num; + std::vector<int>* sequence; +}; + +TEST_F(RunLoopTest, DelayedTaskOrder) { + std::vector<int> sequence; + RunLoop run_loop; + run_loop.PostDelayedTask(Closure(Task(1, &sequence)), 0); + run_loop.PostDelayedTask(Closure(Task(2, &sequence)), 0); + run_loop.PostDelayedTask(Closure(Task(3, &sequence)), 0); + run_loop.RunUntilIdle(); + + ASSERT_EQ(3u, sequence.size()); + EXPECT_EQ(1, sequence[0]); + EXPECT_EQ(2, sequence[1]); + EXPECT_EQ(3, sequence[2]); +} + +struct QuittingTask { + explicit QuittingTask(RunLoop* run_loop) : run_loop(run_loop) {} + + void Run() const { run_loop->Quit(); } + + RunLoop* run_loop; +}; + +TEST_F(RunLoopTest, QuitFromDelayedTask) { + TestRunLoopHandler handler; + MessagePipe test_pipe; + RunLoop run_loop; + run_loop.AddHandler(&handler, + test_pipe.handle0.get(), + MOJO_HANDLE_SIGNAL_READABLE, + MOJO_DEADLINE_INDEFINITE); + run_loop.PostDelayedTask(Closure(QuittingTask(&run_loop)), 0); + run_loop.Run(); +} + +} // namespace +} // namespace mojo diff --git a/mojo/public/cpp/utility/tests/thread_unittest.cc b/mojo/public/cpp/utility/tests/thread_unittest.cc new file mode 100644 index 0000000..57c4ad9 --- /dev/null +++ b/mojo/public/cpp/utility/tests/thread_unittest.cc @@ -0,0 +1,106 @@ +// 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. + +#include "mojo/public/cpp/utility/thread.h" + +#include "mojo/public/cpp/system/macros.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace { + +class SetIntThread : public Thread { + public: + SetIntThread(int* int_to_set, int value) + : int_to_set_(int_to_set), + value_(value) { + } + SetIntThread(const Options& options, int* int_to_set, int value) + : Thread(options), + int_to_set_(int_to_set), + value_(value) { + } + + ~SetIntThread() override {} + + void Run() override { *int_to_set_ = value_; } + + private: + int* const int_to_set_; + const int value_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(SetIntThread); +}; + +TEST(ThreadTest, CreateAndJoin) { + int value = 0; + + // Not starting the thread should result in a no-op. + { + SetIntThread thread(&value, 1234567); + } + EXPECT_EQ(0, value); + + // Start and join. + { + SetIntThread thread(&value, 12345678); + thread.Start(); + thread.Join(); + EXPECT_EQ(12345678, value); + } + + // Ditto, with non-default (but reasonable) stack size. + { + Thread::Options options; + options.set_stack_size(1024 * 1024); // 1 MB. + SetIntThread thread(options, &value, 12345678); + thread.Start(); + thread.Join(); + EXPECT_EQ(12345678, value); + } +} + +// Tests of assertions for Debug builds. +// Note: It's okay to create threads, despite gtest having to fork. (The threads +// are in the child process.) +#if !defined(NDEBUG) +TEST(ThreadTest, DebugAssertionFailures) { + // Can only start once. + EXPECT_DEATH_IF_SUPPORTED({ + int value = 0; + SetIntThread thread(&value, 1); + thread.Start(); + thread.Start(); + }, ""); + + // Must join (if you start). + EXPECT_DEATH_IF_SUPPORTED({ + int value = 0; + SetIntThread thread(&value, 2); + thread.Start(); + }, ""); + + // Can only join once. + EXPECT_DEATH_IF_SUPPORTED({ + int value = 0; + SetIntThread thread(&value, 3); + thread.Start(); + thread.Join(); + thread.Join(); + }, ""); + + // Stack too big (we're making certain assumptions here). + EXPECT_DEATH_IF_SUPPORTED({ + int value = 0; + Thread::Options options; + options.set_stack_size(static_cast<size_t>(-1)); + SetIntThread thread(options, &value, 4); + thread.Start(); + thread.Join(); + }, ""); +} +#endif // !defined(NDEBUG) + +} // namespace +} // namespace mojo diff --git a/mojo/public/cpp/utility/thread.h b/mojo/public/cpp/utility/thread.h new file mode 100644 index 0000000..b7d10ee --- /dev/null +++ b/mojo/public/cpp/utility/thread.h @@ -0,0 +1,62 @@ +// 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. + +#ifndef MOJO_PUBLIC_CPP_UTILITY_THREAD_H_ +#define MOJO_PUBLIC_CPP_UTILITY_THREAD_H_ + +#ifdef _WIN32 +#error "Not implemented: See crbug.com/342893." +#endif + +#include <pthread.h> +#include <stddef.h> + +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { + +// This class is thread-friendly, not thread-safe (e.g., you mustn't call +// |Join()| from multiple threads and/or simultaneously try to destroy the +// object). +class Thread { + public: + // TODO(vtl): Support non-joinable? priority? + class Options { + public: + Options() : stack_size_(0) {} + + // A stack size of 0 means the default. + size_t stack_size() const { return stack_size_; } + void set_stack_size(size_t stack_size) { stack_size_ = stack_size; } + + private: + size_t stack_size_; + + // Copy and assign allowed. + }; + + // TODO(vtl): Add name or name prefix? + Thread(); + explicit Thread(const Options& options); + virtual ~Thread(); + + void Start(); + void Join(); + + virtual void Run() = 0; + + private: + static void* ThreadRunTrampoline(void* arg); + + const Options options_; + pthread_t thread_; + bool started_; + bool joined_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(Thread); +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_UTILITY_THREAD_H_ diff --git a/mojo/public/gles2/BUILD.gn b/mojo/public/gles2/BUILD.gn new file mode 100644 index 0000000..c62ba12 --- /dev/null +++ b/mojo/public/gles2/BUILD.gn @@ -0,0 +1,45 @@ +# Copyright 2015 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. + +# In an is_component_build build, everything can link against //mojo/gles2 +# because it is built as a shared library. However, in a static build, +# //mojo/gles2 is linked into an executable (e.g., mojo_shell), and must be +# injected into other shared libraries (i.e., Mojo Apps) that need the mojo +# gles2 API. +# +# For component targets, add //mojo/public/gles2:for_component to your deps +# section. +# +# For shared_library targets (e.g., a Mojo App), add +# //mojo/public/gles2:for_shared_library to your deps + +group("for_shared_library") { + public_configs = [ "//third_party/khronos:khronos_headers" ] + public_deps = [ + "//mojo/public/c/gles2", + ] + + if (is_component_build) { + deps = [ + "//mojo/gles2", + ] + } else { + deps = [ + "//mojo/public/platform/native:gles2", + ] + } +} + +group("for_component") { + public_configs = [ "//third_party/khronos:khronos_headers" ] + public_deps = [ + "//mojo/public/c/gles2", + ] + + if (is_component_build) { + deps = [ + "//mojo/gles2", + ] + } +} diff --git a/mojo/public/interfaces/BUILD.gn b/mojo/public/interfaces/BUILD.gn new file mode 100644 index 0000000..654145b --- /dev/null +++ b/mojo/public/interfaces/BUILD.gn @@ -0,0 +1,11 @@ +# Copyright 2015 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. + +group("interfaces") { + deps = [ + "application", + "bindings", + "network", + ] +} diff --git a/mojo/public/interfaces/bindings/BUILD.gn b/mojo/public/interfaces/bindings/BUILD.gn new file mode 100644 index 0000000..6d755ea --- /dev/null +++ b/mojo/public/interfaces/bindings/BUILD.gn @@ -0,0 +1,13 @@ +# Copyright 2015 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("../../tools/bindings/mojom.gni") + +mojom("bindings") { + sources = [ + "interface_control_messages.mojom", + ] + + with_environment = false +} diff --git a/mojo/public/interfaces/bindings/interface_control_messages.mojom b/mojo/public/interfaces/bindings/interface_control_messages.mojom new file mode 100644 index 0000000..10bd269 --- /dev/null +++ b/mojo/public/interfaces/bindings/interface_control_messages.mojom @@ -0,0 +1,90 @@ +// Copyright 2015 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. + +[DartPackage="mojo", JavaPackage="org.chromium.mojo.bindings"] +module mojo; + +// For each message pipe representing a user-defined interface, some control +// functions are provided at the same end of the message pipe as the +// user-defined interface, providing information about the user-defined +// interface and controlling behavior of the message pipe. + +//////////////////////////////////////////////////////////////////////////////// +// Run@0xFFFFFFFF(RunInput input) => (RunOutput? output); +// +// This control function runs the input command. If the command is not +// supported, |output| is set to null; otherwise |output| stores the result, +// whose type depends on the input. +// +// TODO(yzshen): Once union support is ready, switch the following definition +// to: +// struct RunMessageParams { +// RunInput input; +// }; +// union RunInput { +// QueryVersion query_version; +// }; +// +// struct RunResponseMessageParams { +// RunOutput? output; +// }; +// union RunOutput { +// QueryVersionResult query_version_result; +// }; + +const uint32 kRunMessageId = 0xFFFFFFFF; + +struct RunMessageParams { + // The reserved fields make the layout compatible with the RunInput union + // described above. + uint32 reserved0; // Must be set to 16. + uint32 reserved1; // Must be set to 0; + QueryVersion query_version; +}; + +struct RunResponseMessageParams { + // The reserved fields make the layout compatible with the RunOutput union + // described above. + uint32 reserved0; // Must be set to 16. + uint32 reserved1; // Must be set to 0. + QueryVersionResult query_version_result; +}; + +// Queries the max supported version of the user-defined interface. +struct QueryVersion { +}; +struct QueryVersionResult { + uint32 version; +}; + +//////////////////////////////////////////////////////////////////////////////// +// RunOrClosePipe@0xFFFFFFFE(RunOrClosePipeInput input); +// +// This control function runs the input command. If the operation fails or the +// command is not supported, the message pipe is closed. +// +// TODO(yzshen): Once union support is ready, switch the following definition +// to: +// struct RunOrClosePipeMessageParams { +// RunOrClosePipeInput input; +// }; +// union RunOrClosePipeInput { +// RequireVersion require_version; +// }; + +const uint32 kRunOrClosePipeMessageId = 0xFFFFFFFE; + +struct RunOrClosePipeMessageParams { + // The reserved fields make the layout compatible with the RunOrClosePipeInput + // union described above. + uint32 reserved0; // Must be set to 16. + uint32 reserved1; // Must be set to 0. + RequireVersion require_version; +}; + +// If the specified version of the user-defined interface is not supported, the +// function fails and the pipe is closed. +struct RequireVersion { + uint32 version; +}; diff --git a/mojo/public/interfaces/bindings/tests/BUILD.gn b/mojo/public/interfaces/bindings/tests/BUILD.gn new file mode 100644 index 0000000..f23b2e4 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/BUILD.gn @@ -0,0 +1,68 @@ +# 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. + +import("../../../tools/bindings/mojom.gni") + +mojom("test_interfaces") { + testonly = true + sources = [ + "math_calculator.mojom", + "no_module.mojom", + "ping_service.mojom", + "rect.mojom", + "regression_tests.mojom", + "sample_factory.mojom", + "sample_import.mojom", + "sample_import2.mojom", + "sample_interfaces.mojom", + "sample_service.mojom", + "scoping.mojom", + "serialization_test_structs.mojom", + "test_constants.mojom", + "test_structs.mojom", + "validation_test_interfaces.mojom", + ] + + with_environment = false +} + +mojom("test_interfaces_experimental") { + testonly = true + sources = [ + "test_unions.mojom", + ] + + with_environment = false +} + +mojom("test_associated_interfaces") { + # These files are not included in the test_interfaces target because + # associated interfaces are not supported by all bindings languages yet. + testonly = true + sources = [ + "test_associated_interfaces.mojom", + ] + + with_environment = false +} + +mojom("versioning_test_service_interfaces") { + # FIXME: Dart packaged applications cannot depend on testonly mojoms. + # testonly = true + sources = [ + "versioning_test_service.mojom", + ] + + with_environment = false +} + +mojom("versioning_test_client_interfaces") { + # FIXME: Dart packaged applications cannot depend on testonly mojoms. + # testonly = true + sources = [ + "versioning_test_client.mojom", + ] + + with_environment = false +} diff --git a/mojo/public/interfaces/bindings/tests/data/message_data b/mojo/public/interfaces/bindings/tests/data/message_data new file mode 100644 index 0000000..b288878 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/message_data @@ -0,0 +1,25 @@ +// File generated by mojo_message_generator. +0X10 +0X00 +0X00 +0X00 +0X02 +0X00 +0X00 +0X00 +0X15 +0X00 +0X00 +0X00 +0X00 +0X00 +0X00 +0X00 +0X09 +0X08 +0X07 +0X06 +0X00 +0X00 +0X00 +0X00 diff --git a/mojo/public/interfaces/bindings/tests/data/validation/boundscheck_msghdr_no_such_method.data b/mojo/public/interfaces/bindings/tests/data/validation/boundscheck_msghdr_no_such_method.data new file mode 100644 index 0000000..30032a1 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/boundscheck_msghdr_no_such_method.data @@ -0,0 +1,7 @@ +[dist4]message_header // num_bytes +[u4]0 // version number +[u4]0 // interface ID +[u4]2 // There is no Method2 +[u4]0 // flags +[u4]0 // padding +[anchr]message_header diff --git a/mojo/public/interfaces/bindings/tests/data/validation/boundscheck_msghdr_no_such_method.expected b/mojo/public/interfaces/bindings/tests/data/validation/boundscheck_msghdr_no_such_method.expected new file mode 100644 index 0000000..a32d895 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/boundscheck_msghdr_no_such_method.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_empty.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_empty.data new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_empty.data diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_empty.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_empty.expected new file mode 100644 index 0000000..779df88 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_empty.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct.data new file mode 100644 index 0000000..68899f4 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct.data @@ -0,0 +1,2 @@ +[u4]24 // num_bytes: Bigger than the total size of the message. +[u4]0 // version diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct.expected new file mode 100644 index 0000000..779df88 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct_header.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct_header.data new file mode 100644 index 0000000..21e7fbc --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct_header.data @@ -0,0 +1 @@ +0x00 diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct_header.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct_header.expected new file mode 100644 index 0000000..779df88 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct_header.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_invalid_flag_combo.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_invalid_flag_combo.data new file mode 100644 index 0000000..dfb2dd2 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_invalid_flag_combo.data @@ -0,0 +1,8 @@ +[dist4]message_header // num_bytes +[u4]1 // version +[u4]0 // interface ID +[u4]0x80000000 // name +[u4]3 // flags: This combination is illegal. +[u4]0 // padding +[u8]1 // request_id +[anchr]message_header diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_invalid_flag_combo.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_invalid_flag_combo.expected new file mode 100644 index 0000000..c33fde3 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_invalid_flag_combo.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_missing_request_id.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_missing_request_id.data new file mode 100644 index 0000000..27804a8 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_missing_request_id.data @@ -0,0 +1,8 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]0x80000000 // name +[u4]1 // flags: This is a response message which expects to + // have a request ID. +[u4]0 // padding +[anchr]message_header diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_missing_request_id.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_missing_request_id.expected new file mode 100644 index 0000000..083db1a --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_missing_request_id.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_no_such_method.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_no_such_method.data new file mode 100644 index 0000000..6302bae --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_no_such_method.data @@ -0,0 +1,7 @@ +[dist4]message_header // num_bytes +[u4]0 // version number +[u4]0 // interface ID +[u4]9999 // There is no Method9999. +[u4]0 // flags +[u4]0 // padding +[anchr]message_header diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_no_such_method.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_no_such_method.expected new file mode 100644 index 0000000..a32d895 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_no_such_method.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_huge.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_huge.data new file mode 100644 index 0000000..2fd0fcd --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_huge.data @@ -0,0 +1,6 @@ +[u4]0xFFFFFFFF // num_bytes: Test whether a huge value will cause overflow. +[u4]0 // version +[u4]0 // interface ID +[u4]0x80000000 // name +[u4]0 // flags +[u4]0 // padding diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_huge.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_huge.expected new file mode 100644 index 0000000..779df88 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_huge.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_min_requirement.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_min_requirement.data new file mode 100644 index 0000000..f58eca9 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_min_requirement.data @@ -0,0 +1,4 @@ +[dist4]message_header // num_bytes: Less than the minimal size of message + // header. +[u4]0 // version +[anchr]message_header diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_min_requirement.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_min_requirement.expected new file mode 100644 index 0000000..25aceee --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_min_requirement.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_struct_header.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_struct_header.data new file mode 100644 index 0000000..e98f66f --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_struct_header.data @@ -0,0 +1,6 @@ +[u4]0 // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]0x80000000 // name +[u4]0 // flags +[u4]0 // padding diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_struct_header.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_struct_header.expected new file mode 100644 index 0000000..25aceee --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_struct_header.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_version_mismatch_1.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_version_mismatch_1.data new file mode 100644 index 0000000..df9e418 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_version_mismatch_1.data @@ -0,0 +1,9 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]0x80000000 // name +[u4]0 // flags +[u4]0 // padding +[u8]0 // Extra bytes that result in mismatched |num_bytes| and + // |version|. +[anchr]message_header diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_version_mismatch_1.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_version_mismatch_1.expected new file mode 100644 index 0000000..25aceee --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_version_mismatch_1.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_version_mismatch_2.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_version_mismatch_2.data new file mode 100644 index 0000000..e2c574e --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_version_mismatch_2.data @@ -0,0 +1,10 @@ +[dist4]message_header // num_bytes +[u4]1 // version +[u4]0 // interface ID +[u4]0x80000000 // name +[u4]1 // flags +[u4]0 // padding +[u8]0 // request_id +[u8]0 // Extra bytes that result in mismatched |num_bytes| and + // |version|. +[anchr]message_header diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_version_mismatch_2.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_version_mismatch_2.expected new file mode 100644 index 0000000..25aceee --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_version_mismatch_2.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_version_mismatch_3.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_version_mismatch_3.data new file mode 100644 index 0000000..f7a321b --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_version_mismatch_3.data @@ -0,0 +1,7 @@ +[dist4]message_header // num_bytes +[u4]8 // version: |num_bytes| is too small for |version|. +[u4]0 // interface ID +[u4]0x80000000 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_version_mismatch_3.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_version_mismatch_3.expected new file mode 100644 index 0000000..25aceee --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_version_mismatch_3.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_good.data new file mode 100644 index 0000000..841da5e --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_good.data @@ -0,0 +1,13 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]0 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method0_params // num_bytes +[u4]0 // version +[f]-1 // param0 +[u4]0 // padding +[anchr]method0_params diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_good.expected new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_good.expected @@ -0,0 +1 @@ +PASS diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct.data new file mode 100644 index 0000000..cff6a30 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct.data @@ -0,0 +1,11 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]0 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[u4]16 // num_bytes: Incomplete struct. +[u4]0 // version +[f]-1 // param0 diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct.expected new file mode 100644 index 0000000..779df88 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct_header.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct_header.data new file mode 100644 index 0000000..3f03ab2 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct_header.data @@ -0,0 +1,9 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]0 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[u4]16 // num_bytes: Incomplete struct header. diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct_header.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct_header.expected new file mode 100644 index 0000000..779df88 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct_header.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags.data new file mode 100644 index 0000000..7aee806 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags.data @@ -0,0 +1,8 @@ +[dist4]message_header // num_bytes +[u4]1 // version +[u4]0 // interface ID +[u4]0 // name +[u4]2 // flags: kMessageIsResponse is set in a request. +[u4]0 // padding +[u8]1 // request_id +[anchr]message_header diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags.expected new file mode 100644 index 0000000..c33fde3 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags2.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags2.data new file mode 100644 index 0000000..5448c5f --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags2.data @@ -0,0 +1,9 @@ +[dist4]message_header // num_bytes +[u4]1 // version +[u4]0 // interface ID +[u4]0 // name +[u4]1 // flags: kMessageExpectsResponse is set in a request + // for a method that does not take a response. +[u4]0 // padding +[u8]1 // request_id +[anchr]message_header diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags2.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags2.expected new file mode 100644 index 0000000..c33fde3 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags2.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_huge.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_huge.data new file mode 100644 index 0000000..4a3e2fe --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_huge.data @@ -0,0 +1,12 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]0 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[u4]0xFFFFFFFF // num_bytes: Test whether a huge value will cause overflow. +[u4]0 // version +[f]-1 // param0 +[u4]0 // padding diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_huge.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_huge.expected new file mode 100644 index 0000000..779df88 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_huge.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_min_requirement.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_min_requirement.data new file mode 100644 index 0000000..fa4c555 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_min_requirement.data @@ -0,0 +1,11 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]0 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method0_params // num_bytes: Less than the minimal size that we expect. +[u4]0 // version +[anchr]method0_params diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_min_requirement.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_min_requirement.expected new file mode 100644 index 0000000..25aceee --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_min_requirement.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_struct_header.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_struct_header.data new file mode 100644 index 0000000..d62206e --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_struct_header.data @@ -0,0 +1,12 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]0 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[u4]4 // num_bytes: Less than the size of struct header. +[u4]0 // version +[f]-1 // param0 +[u4]0 // padding diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_struct_header.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_struct_header.expected new file mode 100644 index 0000000..25aceee --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_struct_header.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_good.data new file mode 100644 index 0000000..5dca2fe --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_good.data @@ -0,0 +1,48 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]10 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method10_params // num_bytes +[u4]0 // version +[dist8]map_data_ptr // param0 +[anchr]method10_params + +[anchr]map_data_ptr +[dist4]map_data_struct_header // num_bytes +[u4]0 // version +[dist8]key_array_ptr +[dist8]value_array_ptr +[anchr]map_data_struct_header + +[anchr]key_array_ptr +[dist4]key_array_member // num_bytes +[u4]2 // num_elements +[dist8]key_string_1 +[dist8]key_string_2 +[anchr]key_array_member + +[anchr]key_string_1 +[dist4]key_string_1_member // num_bytes +[u4]5 // num_elements +0 1 2 3 4 +[anchr]key_string_1_member + +[u4]0 [u4]0 [u1]0 [u1]0 [u1]0 // manual padding for array alignment + +[anchr]key_string_2 +[dist4]key_string_2_member // num_bytes +[u4]5 // num_elements +5 6 7 8 9 +[anchr]key_string_2_member + +[u4]0 [u4]0 [u1]0 [u1]0 [u1]0 // manual padding for array alignment + +[anchr]value_array_ptr +[dist4]value_array_member // num_bytes +[u4]2 // num_elements +1 2 +[anchr]value_array_member diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_good.expected new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_good.expected @@ -0,0 +1 @@ +PASS diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_good_non_unique_keys.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_good_non_unique_keys.data new file mode 100644 index 0000000..f64fbc3 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_good_non_unique_keys.data @@ -0,0 +1,48 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]10 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method10_params // num_bytes +[u4]0 // version +[dist8]map_data_ptr // param0 +[anchr]method10_params + +[anchr]map_data_ptr +[dist4]map_data_struct_header // num_bytes +[u4]0 // version +[dist8]key_array_ptr +[dist8]value_array_ptr +[anchr]map_data_struct_header + +[anchr]key_array_ptr +[dist4]key_array_member // num_bytes +[u4]2 // num_elements +[dist8]key_string_1 +[dist8]key_string_2 +[anchr]key_array_member + +[anchr]key_string_1 +[dist4]key_string_1_member // num_bytes +[u4]5 // num_elements +0 1 2 3 4 +[anchr]key_string_1_member + +[u4]0 [u4]0 [u1]0 [u1]0 [u1]0 // manual padding for array alignment + +[anchr]key_string_2 +[dist4]key_string_2_member // num_bytes +[u4]5 // num_elements +0 1 2 3 4 +[anchr]key_string_2_member + +[u4]0 [u4]0 [u1]0 [u1]0 [u1]0 // manual padding for array alignment + +[anchr]value_array_ptr +[dist4]value_array_member // num_bytes +[u4]2 // num_elements +1 2 +[anchr]value_array_member diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_good_non_unique_keys.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_good_non_unique_keys.expected new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_good_non_unique_keys.expected @@ -0,0 +1 @@ +PASS diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_null_keys.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_null_keys.data new file mode 100644 index 0000000..3a99dc2a --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_null_keys.data @@ -0,0 +1,25 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]10 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method10_params // num_bytes +[u4]0 // version +[dist8]map_data_ptr // param0 +[anchr]method10_params + +[anchr]map_data_ptr +[dist4]map_data_struct_header // num_bytes +[u4]0 // version +[u8]0 // null keys array +[dist8]value_array_ptr +[anchr]map_data_struct_header + +[anchr]value_array_ptr +[dist4]value_array_member // num_bytes +[u4]2 // num_elements +1 2 +[anchr]value_array_member diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_null_keys.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_null_keys.expected new file mode 100644 index 0000000..95d8db0 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_null_keys.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_NULL_POINTER diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_null_values.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_null_values.data new file mode 100644 index 0000000..459d806 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_null_values.data @@ -0,0 +1,40 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]10 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method10_params // num_bytes +[u4]0 // version +[dist8]map_data_ptr // param0 +[anchr]method10_params + +[anchr]map_data_ptr +[dist4]map_data_struct_header // num_bytes +[u4]0 // version +[dist8]key_array_ptr +[u8]0 // null values array +[anchr]map_data_struct_header + +[anchr]key_array_ptr +[dist4]key_array_member // num_bytes +[u4]2 // num_elements +[dist8]key_string_1 +[dist8]key_string_2 +[anchr]key_array_member + +[anchr]key_string_1 +[dist4]key_string_1_member // num_bytes +[u4]5 // num_elements +0 1 2 3 4 +[anchr]key_string_1_member + +[u4]0 [u4]0 [u1]0 [u1]0 [u1]0 // manual padding for array alignment + +[anchr]key_string_2 +[dist4]key_string_2_member // num_bytes +[u4]5 // num_elements +5 6 7 8 9 +[anchr]key_string_2_member diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_null_values.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_null_values.expected new file mode 100644 index 0000000..95d8db0 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_null_values.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_NULL_POINTER diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_one_null_key.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_one_null_key.data new file mode 100644 index 0000000..9127a26 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_one_null_key.data @@ -0,0 +1,40 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]10 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method10_params // num_bytes +[u4]0 // version +[dist8]map_data_ptr // param0 +[anchr]method10_params + +[anchr]map_data_ptr +[dist4]map_data_struct_header // num_bytes +[u4]0 // version +[dist8]key_array_ptr +[dist8]value_array_ptr +[anchr]map_data_struct_header + +[anchr]key_array_ptr +[dist4]key_array_member // num_bytes +[u4]2 // num_elements +[dist8]key_string_1 +[u8]0 // one null key +[anchr]key_array_member + +[anchr]key_string_1 +[dist4]key_string_1_member // num_bytes +[u4]5 // num_elements +0 1 2 3 4 +[anchr]key_string_1_member + +[u4]0 [u4]0 [u1]0 [u1]0 [u1]0 // manual padding for array alignment + +[anchr]value_array_ptr +[dist4]value_array_member // num_bytes +[u4]2 // num_elements +1 2 +[anchr]value_array_member diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_one_null_key.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_one_null_key.expected new file mode 100644 index 0000000..95d8db0 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_one_null_key.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_NULL_POINTER diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_unequal_array_size.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_unequal_array_size.data new file mode 100644 index 0000000..a2f9038 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_unequal_array_size.data @@ -0,0 +1,48 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]10 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method10_params // num_bytes +[u4]0 // version +[dist8]map_data_ptr // param0 +[anchr]method10_params + +[anchr]map_data_ptr +[dist4]map_data_struct_header // num_bytes +[u4]0 // version +[dist8]key_array_ptr +[dist8]value_array_ptr +[anchr]map_data_struct_header + +[anchr]key_array_ptr +[dist4]key_array_member // num_bytes +[u4]2 // num_elements +[dist8]key_string_1 +[dist8]key_string_2 +[anchr]key_array_member + +[anchr]key_string_1 +[dist4]key_string_1_member // num_bytes +[u4]5 // num_elements +0 1 2 3 4 +[anchr]key_string_1_member + +[u4]0 [u4]0 [u1]0 [u1]0 [u1]0 // manual padding for array alignment + +[anchr]key_string_2 +[dist4]key_string_2_member // num_bytes +[u4]5 // num_elements +5 6 7 8 9 +[anchr]key_string_2_member + +[u4]0 [u4]0 [u1]0 [u1]0 [u1]0 // manual padding for array alignment + +[anchr]value_array_ptr +[dist4]value_array_member // num_bytes +[u4]1 // num_elements +1 // unequal size +[anchr]value_array_member diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_unequal_array_size.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_unequal_array_size.expected new file mode 100644 index 0000000..2798d48 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_unequal_array_size.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_DIFFERENT_SIZED_ARRAYS_IN_MAP diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version0.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version0.data new file mode 100644 index 0000000..781079b --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version0.data @@ -0,0 +1,19 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]11 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method11_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[anchr]method11_params + +[anchr]param0_ptr +[dist4]struct_g // num_bytes +[u4]0 // version +[s4]123 // i +[u4]0 // padding +[anchr]struct_g diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version0.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version0.expected new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version0.expected @@ -0,0 +1 @@ +PASS diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version1.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version1.data new file mode 100644 index 0000000..b9ab5bf --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version1.data @@ -0,0 +1,20 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]11 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method11_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[anchr]method11_params + +[anchr]param0_ptr +[dist4]struct_g // num_bytes +[u4]1 // version +[s4]123 // i +[u4]0 // padding +[u8]0 // struct_a +[anchr]struct_g diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version1.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version1.expected new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version1.expected @@ -0,0 +1 @@ +PASS diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version2.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version2.data new file mode 100644 index 0000000..7d61446 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version2.data @@ -0,0 +1,20 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]11 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method11_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[anchr]method11_params + +[anchr]param0_ptr +[dist4]struct_g // num_bytes +[u4]2 // version +[s4]123 // i +[u4]0 // padding +[u8]0 // struct_a +[anchr]struct_g diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version2.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version2.expected new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version2.expected @@ -0,0 +1 @@ +PASS diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version3.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version3.data new file mode 100644 index 0000000..3c3ee12 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version3.data @@ -0,0 +1,28 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]11 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method11_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[anchr]method11_params + +[anchr]param0_ptr +[dist4]struct_g // num_bytes +[u4]3 // version +[s4]123 // i +[b]00000001 // b +0 0 0 // padding +[u8]0 // struct_a +[dist8]str_ptr // str +[anchr]struct_g + +[anchr]str_ptr +[dist4]string // num_bytes +[u4]2 // num_elements +0 1 +[anchr]string diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version3.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version3.expected new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version3.expected @@ -0,0 +1 @@ +PASS diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version_newer_than_known_1.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version_newer_than_known_1.data new file mode 100644 index 0000000..2e9fde6 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version_newer_than_known_1.data @@ -0,0 +1,30 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]11 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method11_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[anchr]method11_params + +[anchr]param0_ptr +[dist4]struct_g // num_bytes +[u4]5 // version: Newer than what the validator knows. + // It is okay that the size is the same as the latest + // version that the validator knows. +[s4]123 // i +[b]00000001 // b +0 0 0 // padding +[u8]0 // struct_a +[dist8]str_ptr // str +[anchr]struct_g + +[anchr]str_ptr +[dist4]string // num_bytes +[u4]2 // num_elements +0 1 +[anchr]string diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version_newer_than_known_1.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version_newer_than_known_1.expected new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version_newer_than_known_1.expected @@ -0,0 +1 @@ +PASS diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version_newer_than_known_2.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version_newer_than_known_2.data new file mode 100644 index 0000000..9a95626 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version_newer_than_known_2.data @@ -0,0 +1,30 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]11 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method11_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[anchr]method11_params + +[anchr]param0_ptr +[dist4]struct_g // num_bytes +[u4]5 // version: Newer than what the validator knows. +[s4]123 // i +[b]00000001 // b +0 0 0 // padding +[u8]0 // struct_a +[dist8]str_ptr // str +[u8]0 // unknown contents +[u8]0 // unknown contents +[anchr]struct_g + +[anchr]str_ptr +[dist4]string // num_bytes +[u4]2 // num_elements +0 1 +[anchr]string diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version_newer_than_known_2.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version_newer_than_known_2.expected new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version_newer_than_known_2.expected @@ -0,0 +1 @@ +PASS diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_num_bytes_version_mismatch_1.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_num_bytes_version_mismatch_1.data new file mode 100644 index 0000000..c2e5a8d --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_num_bytes_version_mismatch_1.data @@ -0,0 +1,21 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]11 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method11_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[anchr]method11_params + +[anchr]param0_ptr +[dist4]struct_g // num_bytes: The size is too big for the version. +[u4]1 // version +[s4]123 // i +[u4]0 // padding +[u8]0 // struct_a +[u8]0 // Unexpected contents that cause the mismatch. +[anchr]struct_g diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_num_bytes_version_mismatch_1.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_num_bytes_version_mismatch_1.expected new file mode 100644 index 0000000..25aceee --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_num_bytes_version_mismatch_1.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_num_bytes_version_mismatch_2.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_num_bytes_version_mismatch_2.data new file mode 100644 index 0000000..edfe5fa --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_num_bytes_version_mismatch_2.data @@ -0,0 +1,19 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]11 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method11_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[anchr]method11_params + +[anchr]param0_ptr +[dist4]struct_g // num_bytes: The size is too small for the version. +[u4]2 // version +[s4]123 // i +[u4]0 // padding +[anchr]struct_g diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_num_bytes_version_mismatch_2.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_num_bytes_version_mismatch_2.expected new file mode 100644 index 0000000..25aceee --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_num_bytes_version_mismatch_2.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd12_invalid_request_flags.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd12_invalid_request_flags.data new file mode 100644 index 0000000..13135ae --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd12_invalid_request_flags.data @@ -0,0 +1,9 @@ +[dist4]message_header // num_bytes +[u4]1 // version +[u4]0 // interface ID +[u4]12 // name +[u4]0 // flags: kMessageExpectsResponse is not set but + // expected. +[u4]0 // padding +[u8]1 // request_id +[anchr]message_header diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd12_invalid_request_flags.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd12_invalid_request_flags.expected new file mode 100644 index 0000000..c33fde3 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd12_invalid_request_flags.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd13_good_1.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd13_good_1.data new file mode 100644 index 0000000..51973be --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd13_good_1.data @@ -0,0 +1,17 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]13 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method13_params // num_bytes +[u4]0 // version +[u4]0xFFFFFFFF // param0 +[u4]1234 +[u4]65535 // param1 +[u4]0xFFFFFFFF // param2 +[u4]3242 +[u4]0 // padding +[anchr]method13_params diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd13_good_1.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd13_good_1.expected new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd13_good_1.expected @@ -0,0 +1 @@ +PASS diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd13_good_2.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd13_good_2.data new file mode 100644 index 0000000..b739731 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd13_good_2.data @@ -0,0 +1,19 @@ +[handles]2 + +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]13 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method13_params // num_bytes +[u4]0 // version +[u4]0 // param0 +[u4]1234 +[u4]65535 // param1 +[u4]1 // param2 +[u4]3242 +[u4]0 // padding +[anchr]method13_params diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd13_good_2.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd13_good_2.expected new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd13_good_2.expected @@ -0,0 +1 @@ +PASS diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_good.data new file mode 100644 index 0000000..b6c201a --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_good.data @@ -0,0 +1,18 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]1 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method1_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[anchr]method1_params + +[anchr]param0_ptr +[dist4]struct_a // num_bytes +[u4]0 // version +[u8]1234 // i +[anchr]struct_a diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_good.expected new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_good.expected @@ -0,0 +1 @@ +PASS diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_misaligned_struct.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_misaligned_struct.data new file mode 100644 index 0000000..ec39b71 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_misaligned_struct.data @@ -0,0 +1,20 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]1 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method1_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[anchr]method1_params + +[u1]0 // Causes the following struct to be misaligned. + +[anchr]param0_ptr +[dist4]struct_a // num_bytes +[u4]0 // version +[u8]1234 // i +[anchr]struct_a diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_misaligned_struct.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_misaligned_struct.expected new file mode 100644 index 0000000..acca999 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_misaligned_struct.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_MISALIGNED_OBJECT diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_struct_pointer_overflow.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_struct_pointer_overflow.data new file mode 100644 index 0000000..6d92050 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_struct_pointer_overflow.data @@ -0,0 +1,13 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]1 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method1_params // num_bytes +[u4]0 // version +[u8]0xFFFFFFFFFFFFFFFF // param0: Test whether decoding the pointer causes + // overflow. +[anchr]method1_params diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_struct_pointer_overflow.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_struct_pointer_overflow.expected new file mode 100644 index 0000000..23abb8c --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_struct_pointer_overflow.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_ILLEGAL_POINTER diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_unexpected_null_struct.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_unexpected_null_struct.data new file mode 100644 index 0000000..569733b --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_unexpected_null_struct.data @@ -0,0 +1,12 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]1 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method1_params // num_bytes +[u4]0 // version +[u8]0 // param0: An unexpected null pointer. +[anchr]method1_params diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_unexpected_null_struct.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_unexpected_null_struct.expected new file mode 100644 index 0000000..95d8db0 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_unexpected_null_struct.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_NULL_POINTER diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_good.data new file mode 100644 index 0000000..40719f5 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_good.data @@ -0,0 +1,34 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]2 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method2_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[dist8]param1_ptr // param1 +[anchr]method2_params + +[anchr]param0_ptr +[dist4]struct_b // num_bytes +[u4]0 // version +[dist8]struct_a_ptr // struct_a +[anchr]struct_b + +[u8]0 // Having extra bytes in the middle is okay if the following objects are + // still properly alignmented. + +[anchr]struct_a_ptr +[dist4]struct_a_member // num_bytes +[u4]0 // version +[u8]12345 // i +[anchr]struct_a_member + +[anchr]param1_ptr +[dist4]struct_a_param // num_bytes +[u4]0 // version +[u8]67890 // i +[anchr]struct_a_param diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_good.expected new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_good.expected @@ -0,0 +1 @@ +PASS diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_multiple_pointers_to_same_struct.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_multiple_pointers_to_same_struct.data new file mode 100644 index 0000000..ef6525b --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_multiple_pointers_to_same_struct.data @@ -0,0 +1,27 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]2 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method2_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[dist8]param1_ptr // param1 +[anchr]method2_params + +[anchr]param0_ptr +[dist4]struct_b // num_bytes +[u4]0 // version +[dist8]struct_a_ptr // struct_a +[anchr]struct_b + +// There are two pointers pointing to the same struct. +[anchr]struct_a_ptr +[anchr]param1_ptr +[dist4]struct_a // num_bytes +[u4]0 // version +[u8]12345 // i +[anchr]struct_a diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_multiple_pointers_to_same_struct.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_multiple_pointers_to_same_struct.expected new file mode 100644 index 0000000..779df88 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_multiple_pointers_to_same_struct.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_overlapped_objects.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_overlapped_objects.data new file mode 100644 index 0000000..58b25a1 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_overlapped_objects.data @@ -0,0 +1,32 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]2 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method2_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[dist8]param1_ptr // param1 +[anchr]method2_params + +[anchr]param0_ptr +[dist4]struct_b // num_bytes +[u4]0 // version +[dist8]struct_a_ptr // struct_a +[anchr]struct_b + +[anchr]struct_a_ptr +[dist4]struct_a_member // num_bytes +[u4]0 // version + +[anchr]param1_ptr +// The following |num_bytes| and |version| fields are also the |i| field of the +// previous struct. +[dist4]struct_a_param // num_bytes +[u4]0 // version +[anchr]struct_a_member +[u8]67890 // i +[anchr]struct_a_param diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_overlapped_objects.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_overlapped_objects.expected new file mode 100644 index 0000000..779df88 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_overlapped_objects.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_wrong_layout_order.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_wrong_layout_order.data new file mode 100644 index 0000000..3038ed8 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_wrong_layout_order.data @@ -0,0 +1,34 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]2 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method2_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[dist8]param1_ptr // param1 +[anchr]method2_params + +[anchr]param0_ptr +[dist4]struct_b // num_bytes +[u4]0 // version +[dist8]struct_a_ptr // struct_a +[anchr]struct_b + +// The following two structs are arranged in wrong order. + +[anchr]param1_ptr +[dist4]struct_a_param // num_bytes +[u4]0 // version +[u8]67890 // i +[anchr]struct_a_param + +[anchr]struct_a_ptr +[dist4]struct_a_member // num_bytes +[u4]0 // version +[u8]12345 // i +[anchr]struct_a_member + diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_wrong_layout_order.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_wrong_layout_order.expected new file mode 100644 index 0000000..779df88 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_wrong_layout_order.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_huge.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_huge.data new file mode 100644 index 0000000..6814636 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_huge.data @@ -0,0 +1,18 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]3 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method3_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[anchr]method3_params + +[anchr]param0_ptr +[u4]0xFFFFFFFF // num_bytes: Test whether a huge value will cause overflow. +[u4]12 // num_elements +[b]01010101 +[b]00001111 diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_huge.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_huge.expected new file mode 100644 index 0000000..779df88 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_huge.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_array_header.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_array_header.data new file mode 100644 index 0000000..45021c0 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_array_header.data @@ -0,0 +1,18 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]3 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method3_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[anchr]method3_params + +[anchr]param0_ptr +[u4]7 // num_bytes: Less than the size of array header. +[u4]12 // num_elements +[b]01010101 +[b]00001111 diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_array_header.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_array_header.expected new file mode 100644 index 0000000..5a1ec4e --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_array_header.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_necessary_size.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_necessary_size.data new file mode 100644 index 0000000..3d38702 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_necessary_size.data @@ -0,0 +1,20 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]3 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method3_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[anchr]method3_params + +[anchr]param0_ptr +[dist4]array // num_bytes: Less than the size needed (array header + 12 boolean + // values). +[u4]12 // num_elements +[b]01010101 +[anchr]array +[b]00001111 diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_necessary_size.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_necessary_size.expected new file mode 100644 index 0000000..5a1ec4e --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_necessary_size.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_pointer_overflow.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_pointer_overflow.data new file mode 100644 index 0000000..2f9e091 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_pointer_overflow.data @@ -0,0 +1,13 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]3 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method3_params // num_bytes +[u4]0 // version +[u8]0xFFFFFFFFFFFFFFFF // param0: Test whether decoding the pointer causes + // overflow. +[anchr]method3_params diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_pointer_overflow.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_pointer_overflow.expected new file mode 100644 index 0000000..23abb8c --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_pointer_overflow.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_ILLEGAL_POINTER diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_good.data new file mode 100644 index 0000000..ad26763 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_good.data @@ -0,0 +1,19 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]3 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method3_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[anchr]method3_params + +[anchr]param0_ptr +[dist4]array // num_bytes +[u4]12 // num_elements +[b]01010101 +[b]00001111 +[anchr]array diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_good.expected new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_good.expected @@ -0,0 +1 @@ +PASS diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array.data new file mode 100644 index 0000000..d773458 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array.data @@ -0,0 +1,16 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]3 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method3_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[anchr]method3_params + +[anchr]param0_ptr +[u4]16 // num_bytes +[u1]0 // num_elements: Incomplete array. diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array.expected new file mode 100644 index 0000000..779df88 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array_header.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array_header.data new file mode 100644 index 0000000..ca462a5 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array_header.data @@ -0,0 +1,15 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]3 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method3_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[anchr]method3_params + +[anchr]param0_ptr +[u4]16 // num_bytes: Incomplete array header. diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array_header.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array_header.expected new file mode 100644 index 0000000..779df88 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array_header.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_misaligned_array.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_misaligned_array.data new file mode 100644 index 0000000..5adfbba --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_misaligned_array.data @@ -0,0 +1,21 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]3 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method3_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[anchr]method3_params + +[u2]0 // Causes the following array to be misaligned. + +[anchr]param0_ptr +[dist4]array // num_bytes +[u4]12 // num_elements +[b]01010101 +[b]00001111 +[anchr]array diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_misaligned_array.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_misaligned_array.expected new file mode 100644 index 0000000..acca999 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_misaligned_array.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_MISALIGNED_OBJECT diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_unexpected_null_array.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_unexpected_null_array.data new file mode 100644 index 0000000..0f96c4b --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_unexpected_null_array.data @@ -0,0 +1,12 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]3 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method3_params // num_bytes +[u4]0 // version +[u8]0 // param0: An unexpected null pointer. +[anchr]method3_params diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_unexpected_null_array.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_unexpected_null_array.expected new file mode 100644 index 0000000..95d8db0 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_unexpected_null_array.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_NULL_POINTER diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_good.data new file mode 100644 index 0000000..84943d2 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_good.data @@ -0,0 +1,34 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]4 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method4_params // num_bytes: Larger than what we know is okay. +[u4]3 // version: Larger than what we know is okay. +[dist8]param0_ptr // param0 +[dist8]param1_ptr // param1 +[u8]0 // unknown +[anchr]method4_params + +[anchr]param0_ptr +[dist4]struct_c // num_bytes +[u4]0 // version +[dist8]array_ptr // array +[anchr]struct_c + +[anchr]array_ptr +[dist4]array_member // num_bytes +[u4]3 // num_elements +0 1 2 +[anchr]array_member + +[u4]0 [u1]0 // Padding to make the next array aligned properly. + +[anchr]param1_ptr +[dist4]array_param // num_bytes +[u4]10 // num_elements +0 1 2 3 4 5 6 7 8 9 +[anchr]array_param diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_good.expected new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_good.expected @@ -0,0 +1 @@ +PASS diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_multiple_pointers_to_same_array.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_multiple_pointers_to_same_array.data new file mode 100644 index 0000000..2f84185 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_multiple_pointers_to_same_array.data @@ -0,0 +1,26 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]4 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method4_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[dist8]param1_ptr // param1 +[anchr]method4_params + +[anchr]param0_ptr +[dist4]struct_c // num_bytes +[u4]0 // version +[dist8]array_ptr // array +[anchr]struct_c + +[anchr]param1_ptr +[anchr]array_ptr +[dist4]array_member // num_bytes +[u4]3 // num_elements +0 1 2 +[anchr]array_member diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_multiple_pointers_to_same_array.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_multiple_pointers_to_same_array.expected new file mode 100644 index 0000000..779df88 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_multiple_pointers_to_same_array.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_overlapped_objects.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_overlapped_objects.data new file mode 100644 index 0000000..d863e64a --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_overlapped_objects.data @@ -0,0 +1,32 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]4 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method4_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[dist8]param1_ptr // param1 +[anchr]method4_params + +[anchr]param0_ptr +[dist4]struct_c // num_bytes +[u4]0 // version +[dist8]array_ptr // array +[anchr]struct_c + +[anchr]array_ptr +[dist4]array_member // num_bytes +[u4]3 // num_elements + +[anchr]param1_ptr +// The first three bytes of |num_bytes| are also the elements of the previous +// array. +[dist4]array_param // num_bytes +[u4]10 // num_elements +0 1 2 3 4 5 6 7 8 9 +[anchr]array_param +[anchr]array_member diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_overlapped_objects.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_overlapped_objects.expected new file mode 100644 index 0000000..779df88 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_overlapped_objects.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_wrong_layout_order.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_wrong_layout_order.data new file mode 100644 index 0000000..b61423a --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_wrong_layout_order.data @@ -0,0 +1,35 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]4 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method4_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[dist8]param1_ptr // param1 +[anchr]method4_params + +[anchr]param0_ptr +[dist4]struct_c // num_bytes +[u4]0 // version +[dist8]array_ptr // array +[anchr]struct_c + +// The following two arrays are arranged in wrong order. + +[anchr]param1_ptr +[dist4]array_param // num_bytes +[u4]10 // num_elements +0 1 2 3 4 5 6 7 8 9 +[anchr]array_param + +[u4]0 [u2]0 // Padding to make the next array aligned properly. + +[anchr]array_ptr +[dist4]array_member // num_bytes +[u4]3 // num_elements +0 1 2 +[anchr]array_member diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_wrong_layout_order.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_wrong_layout_order.expected new file mode 100644 index 0000000..779df88 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_wrong_layout_order.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_good.data new file mode 100644 index 0000000..dcec895 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_good.data @@ -0,0 +1,37 @@ +[handles]10 // Larger than the number of handles that we know about is okay. + +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]5 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method5_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[u4]4 // param1 +[u4]0 // padding +[anchr]method5_params + +[anchr]param0_ptr +[dist4]struct_e // num_bytes +[u4]0 // version +[dist8]struct_d_ptr // struct_d +[u4]3 // data_pipe_consumer +[u4]0 // padding +[anchr]struct_e + +[anchr]struct_d_ptr +[dist4]struct_d // num_bytes +[u4]0 // version +[dist8]message_pipes_ptr // message_pipes +[anchr]struct_d + +[anchr]message_pipes_ptr +[dist4]message_pipe_array // num_bytes +[u4]2 // num_elements +[u4]0 +[u4]1 +[anchr]message_pipe_array diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_good.expected new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_good.expected @@ -0,0 +1 @@ +PASS diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_handle_out_of_range.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_handle_out_of_range.data new file mode 100644 index 0000000..d4a82ed --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_handle_out_of_range.data @@ -0,0 +1,38 @@ +[handles]10 + +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]5 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method5_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[u4]10 // param1: It is outside of the valid encoded handle + // range [0, 10). +[u4]0 // padding +[anchr]method5_params + +[anchr]param0_ptr +[dist4]struct_e // num_bytes +[u4]0 // version +[dist8]struct_d_ptr // struct_d +[u4]3 // data_pipe_consumer +[u4]0 // padding +[anchr]struct_e + +[anchr]struct_d_ptr +[dist4]struct_d // num_bytes +[u4]0 // version +[dist8]message_pipes_ptr // message_pipes +[anchr]struct_d + +[anchr]message_pipes_ptr +[dist4]message_pipe_array // num_bytes +[u4]2 // num_elements +[u4]0 +[u4]1 +[anchr]message_pipe_array diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_handle_out_of_range.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_handle_out_of_range.expected new file mode 100644 index 0000000..eef8e38 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_handle_out_of_range.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_ILLEGAL_HANDLE diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_1.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_1.data new file mode 100644 index 0000000..9ee7a48 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_1.data @@ -0,0 +1,37 @@ +[handles]10 + +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]5 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method5_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[u4]4 // param1 +[u4]0 // padding +[anchr]method5_params + +[anchr]param0_ptr +[dist4]struct_e // num_bytes +[u4]0 // version +[dist8]struct_d_ptr // struct_d +[u4]4 // data_pipe_consumer: The same value as |param1| above. +[u4]0 // padding +[anchr]struct_e + +[anchr]struct_d_ptr +[dist4]struct_d // num_bytes +[u4]0 // version +[dist8]message_pipes_ptr // message_pipes +[anchr]struct_d + +[anchr]message_pipes_ptr +[dist4]message_pipe_array // num_bytes +[u4]2 // num_elements +[u4]0 +[u4]1 +[anchr]message_pipe_array diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_1.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_1.expected new file mode 100644 index 0000000..eef8e38 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_1.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_ILLEGAL_HANDLE diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_2.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_2.data new file mode 100644 index 0000000..cb01cae --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_2.data @@ -0,0 +1,37 @@ +[handles]10 + +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]5 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method5_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[u4]4 // param1 +[u4]0 // padding +[anchr]method5_params + +[anchr]param0_ptr +[dist4]struct_e // num_bytes +[u4]0 // version +[dist8]struct_d_ptr // struct_d +[u4]3 // data_pipe_consumer +[u4]0 // padding +[anchr]struct_e + +[anchr]struct_d_ptr +[dist4]struct_d // num_bytes +[u4]0 // version +[dist8]message_pipes_ptr // message_pipes +[anchr]struct_d + +[anchr]message_pipes_ptr +[dist4]message_pipe_array // num_bytes +[u4]2 // num_elements +[u4]1 // The two message pipe handles have the same value. +[u4]1 +[anchr]message_pipe_array diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_2.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_2.expected new file mode 100644 index 0000000..eef8e38 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_2.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_ILLEGAL_HANDLE diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_unexpected_invalid_handle.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_unexpected_invalid_handle.data new file mode 100644 index 0000000..b06ae0a --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_unexpected_invalid_handle.data @@ -0,0 +1,36 @@ +[handles]5 + +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]5 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method5_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[u4]4 // param1 +[u4]0 // padding +[anchr]method5_params + +[anchr]param0_ptr +[dist4]struct_e // num_bytes +[u4]0 // version +[dist8]struct_d_ptr // struct_d +[s4]-1 // data_pipe_consumer: An unexpected invalid handle. +[u4]0 // padding +[anchr]struct_e + +[anchr]struct_d_ptr +[dist4]struct_d // num_bytes +[u4]0 // version +[dist8]message_pipes_ptr // message_pipes +[anchr]struct_d + +[anchr]message_pipes_ptr +[dist4]message_pipe_array // num_bytes +[u4]1 // num_elements +[u4]2 +[anchr]message_pipe_array diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_unexpected_invalid_handle.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_unexpected_invalid_handle.expected new file mode 100644 index 0000000..6768236 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_unexpected_invalid_handle.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_wrong_handle_order.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_wrong_handle_order.data new file mode 100644 index 0000000..f641de0 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_wrong_handle_order.data @@ -0,0 +1,38 @@ +[handles]10 + +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]5 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method5_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[u4]9 // param1 +[u4]0 // padding +[anchr]method5_params + +[anchr]param0_ptr +[dist4]struct_e // num_bytes +[u4]0 // version +[dist8]struct_d_ptr // struct_d +[u4]1 // data_pipe_consumer: It is smaller than those handles + // in |message_pipe_array|, which is wrong. +[u4]0 // padding +[anchr]struct_e + +[anchr]struct_d_ptr +[dist4]struct_d // num_bytes +[u4]0 // version +[dist8]message_pipes_ptr // message_pipes +[anchr]struct_d + +[anchr]message_pipes_ptr +[dist4]message_pipe_array // num_bytes +[u4]2 // num_elements +[u4]3 +[u4]4 +[anchr]message_pipe_array diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_wrong_handle_order.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_wrong_handle_order.expected new file mode 100644 index 0000000..eef8e38 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_wrong_handle_order.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_ILLEGAL_HANDLE diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_good.data new file mode 100644 index 0000000..fb3f862 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_good.data @@ -0,0 +1,24 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]6 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method6_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[anchr]method6_params + +[anchr]param0_ptr +[dist4]array_param // num_bytes +[u4]1 // num_elements +[dist8]element_ptr +[anchr]array_param + +[anchr]element_ptr +[dist4]array_element // num_bytes +[u4]10 // num_elements +0 1 2 3 4 5 6 7 8 9 +[anchr]array_element diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_good.expected new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_good.expected @@ -0,0 +1 @@ +PASS diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_nested_array_num_bytes_less_than_necessary_size.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_nested_array_num_bytes_less_than_necessary_size.data new file mode 100644 index 0000000..c8cacf0 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_nested_array_num_bytes_less_than_necessary_size.data @@ -0,0 +1,24 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]6 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method6_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[anchr]method6_params + +[anchr]param0_ptr +[dist4]array_param // num_bytes +[u4]1 // num_elements +[dist8]element_ptr +[anchr]array_param + +[anchr]element_ptr +[dist4]array_element // num_bytes: It is insufficient to store 12 elements. +[u4]12 // num_elements +0 1 2 3 4 5 6 7 8 9 +[anchr]array_element diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_nested_array_num_bytes_less_than_necessary_size.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_nested_array_num_bytes_less_than_necessary_size.expected new file mode 100644 index 0000000..5a1ec4e --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_nested_array_num_bytes_less_than_necessary_size.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_good.data new file mode 100644 index 0000000..c972676 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_good.data @@ -0,0 +1,40 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]7 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method7_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[dist8]param1_ptr // param1 +[anchr]method7_params + +[anchr]param0_ptr +[dist4]struct_f // num_bytes +[u4]0 // version +[dist8]array_ptr // fixed_size_array +[anchr]struct_f + +[anchr]array_ptr +[dist4]array_member // num_bytes +[u4]3 // num_elements +0 1 2 +[anchr]array_member + +[u4]0 [u1]0 // Padding to make the next array aligned properly. + +[anchr]param1_ptr +[dist4]array_param // num_bytes +[u4]2 // num_elements +[u8]0 // A null pointer, which is okay. +[dist8]array_element_ptr +[anchr]array_param + +[anchr]array_element_ptr +[dist4]array_element // num_bytes +[u4]3 // num_elements +0 1 2 +[anchr]array_element diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_good.expected new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_good.expected @@ -0,0 +1 @@ +PASS diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unexpected_null_fixed_array.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unexpected_null_fixed_array.data new file mode 100644 index 0000000..4d25cf2 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unexpected_null_fixed_array.data @@ -0,0 +1,26 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]7 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method7_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[dist8]param1_ptr // param1 +[anchr]method7_params + +[anchr]param0_ptr +[dist4]struct_f // num_bytes +[u4]0 // version +[u8]0 // fixed_size_array: An unexpected null pointer. +[anchr]struct_f + +[anchr]param1_ptr +[dist4]array_param // num_bytes +[u4]2 // num_elements +[u8]0 // A null pointer, which is okay. +[u8]0 // A null pointer, which is okay. +[anchr]array_param diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unexpected_null_fixed_array.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unexpected_null_fixed_array.expected new file mode 100644 index 0000000..95d8db0 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unexpected_null_fixed_array.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_NULL_POINTER diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unmatched_array_elements.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unmatched_array_elements.data new file mode 100644 index 0000000..cee6e14 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unmatched_array_elements.data @@ -0,0 +1,34 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]7 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method7_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[dist8]param1_ptr // param1 +[anchr]method7_params + +[anchr]param0_ptr +[dist4]struct_f // num_bytes +[u4]0 // version +[dist8]array_ptr // fixed_size_array +[anchr]struct_f + +[anchr]array_ptr +[dist4]array_member // num_bytes +[u4]2 // num_elements: Too few elements. +0 1 +[anchr]array_member + +[u4]0 [u1]0 [u1]0 // Padding for alignment of next array. + +[anchr]param1_ptr +[dist4]array_param // num_bytes +[u4]2 // num_elements +[u8]0 // A null pointer, which is okay. +[u8]0 // A null pointer, which is okay. +[anchr]array_param diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unmatched_array_elements.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unmatched_array_elements.expected new file mode 100644 index 0000000..5a1ec4e --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unmatched_array_elements.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unmatched_array_elements_nested.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unmatched_array_elements_nested.data new file mode 100644 index 0000000..3095a73 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unmatched_array_elements_nested.data @@ -0,0 +1,40 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]7 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method7_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[dist8]param1_ptr // param1 +[anchr]method7_params + +[anchr]param0_ptr +[dist4]struct_f // num_bytes +[u4]0 // version +[dist8]array_ptr // fixed_size_array +[anchr]struct_f + +[anchr]array_ptr +[dist4]array_member // num_bytes +[u4]3 // num_elements +0 1 3 +[anchr]array_member + +[u4]0 [u1]0 // Padding for alignment of next array. + +[anchr]param1_ptr +[dist4]array_param // num_bytes +[u4]2 // num_elements +[dist8]array_element_ptr +[u8]0 // A null pointer, which is okay. +[anchr]array_param + +[anchr]array_element_ptr +[dist4]array_element // num_bytes +[u4]4 // num_elements: Too many elements. +0 1 2 3 +[anchr]array_element diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unmatched_array_elements_nested.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unmatched_array_elements_nested.expected new file mode 100644 index 0000000..5a1ec4e --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unmatched_array_elements_nested.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_array_num_bytes_overflow.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_array_num_bytes_overflow.data new file mode 100644 index 0000000..b19e141 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_array_num_bytes_overflow.data @@ -0,0 +1,21 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]8 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method8_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[anchr]method8_params + +[anchr]param0_ptr +[dist4]array_param // num_bytes +[u4]0x20000001 // num_elements: The corresponding array size should be + // 0x20000001 * 8 + 8 = 0x100000010 which is + // 2^32 + 16 (base-10), while |num_bytes| is a 32-bit + // unsigned integer and its value is 16. +[u8]0 +[anchr]array_param diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_array_num_bytes_overflow.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_array_num_bytes_overflow.expected new file mode 100644 index 0000000..5a1ec4e --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_array_num_bytes_overflow.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_good.data new file mode 100644 index 0000000..08c4bc3 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_good.data @@ -0,0 +1,32 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]8 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method8_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[anchr]method8_params + +[anchr]param0_ptr +[dist4]array_param // num_bytes +[u4]3 // num_elements +[u8]0 // A null pointer, which is okay. +[dist8]nested_array_ptr +[u8]0 // A null pointer, which is okay. +[anchr]array_param + +[anchr]nested_array_ptr +[dist4]nested_array // num_bytes +[u4]1 // num_elements +[dist8]string_ptr +[anchr]nested_array + +[anchr]string_ptr +[dist4]string // num_bytes +[u4]5 // num_elements +0 1 2 3 4 +[anchr]string diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_good.expected new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_good.expected @@ -0,0 +1 @@ +PASS diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_array.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_array.data new file mode 100644 index 0000000..03f2a10 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_array.data @@ -0,0 +1,12 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]8 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method8_params // num_bytes +[u4]0 // version +[u8]0 // param0: An unexpected null pointer. +[anchr]method8_params diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_array.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_array.expected new file mode 100644 index 0000000..95d8db0 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_array.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_NULL_POINTER diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_string.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_string.data new file mode 100644 index 0000000..b1b4462 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_string.data @@ -0,0 +1,33 @@ +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]8 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method8_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[anchr]method8_params + +[anchr]param0_ptr +[dist4]array_param // num_bytes +[u4]3 // num_elements +[u8]0 // A null pointer, which is okay. +[dist8]nested_array_ptr +[u8]0 // A null pointer, which is okay. +[anchr]array_param + +[anchr]nested_array_ptr +[dist4]nested_array // num_bytes +[u4]2 // num_elements +[dist8]string_ptr +[u8]0 // An unexpected null pointer. +[anchr]nested_array + +[anchr]string_ptr +[dist4]string // num_bytes +[u4]5 // num_elements +0 1 2 3 4 +[anchr]string diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_string.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_string.expected new file mode 100644 index 0000000..95d8db0 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_string.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_NULL_POINTER diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good.data new file mode 100644 index 0000000..6ed0009 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good.data @@ -0,0 +1,36 @@ +[handles]4 + +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]9 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method9_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[anchr]method9_params + +[anchr]param0_ptr +[dist4]array_param // num_bytes +[u4]2 // num_elements +[dist8]nested_array_ptr_0 +[dist8]nested_array_ptr_1 +[anchr]array_param + +[anchr]nested_array_ptr_0 +[dist4]nested_array_0 // num_bytes +[u4]2 // num_elements +[u4]0 +[s4]-1 // An invalid handle, which is okay. +[anchr]nested_array_0 + +[anchr]nested_array_ptr_1 +[dist4]nested_array_1 // num_bytes +[u4]3 // num_elements +[u4]2 +[s4]-1 // An invalid handle, which is okay. +[u4]3 +[anchr]nested_array_1 diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good.expected new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good.expected @@ -0,0 +1 @@ +PASS diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good_null_array.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good_null_array.data new file mode 100644 index 0000000..90feced --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good_null_array.data @@ -0,0 +1,14 @@ +[handles]4 + +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]9 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method9_params // num_bytes +[u4]0 // version +[u8]0 // param0: A null pointer, which is okay. +[anchr]method9_params diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good_null_array.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good_null_array.expected new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good_null_array.expected @@ -0,0 +1 @@ +PASS diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_unexpected_null_array.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_unexpected_null_array.data new file mode 100644 index 0000000..e87fbcb --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_unexpected_null_array.data @@ -0,0 +1,27 @@ +[handles]4 + +[dist4]message_header // num_bytes +[u4]0 // version +[u4]0 // interface ID +[u4]9 // name +[u4]0 // flags +[u4]0 // padding +[anchr]message_header + +[dist4]method9_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[anchr]method9_params + +[anchr]param0_ptr +[dist4]array_param // num_bytes +[u4]2 // num_elements +[dist8]nested_array_ptr_0 +[u8]0 // An unexpected null pointer. +[anchr]array_param + +[anchr]nested_array_ptr_0 +[dist4]nested_array_0 // num_bytes +[u4]1 // num_elements +[u4]0 +[anchr]nested_array_0 diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_unexpected_null_array.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_unexpected_null_array.expected new file mode 100644 index 0000000..95d8db0 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_unexpected_null_array.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_NULL_POINTER diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_resp_mthd0_good.data b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_resp_mthd0_good.data new file mode 100644 index 0000000..7da356f --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_resp_mthd0_good.data @@ -0,0 +1,19 @@ +[dist4]message_header // num_bytes +[u4]1 // version +[u4]0 // interface ID +[u4]0 // name +[u4]2 // flags kMessageIsResponse +[u4]0 // padding +[u8]1 // request_id +[anchr]message_header + +[dist4]method0_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[anchr]method0_params + +[anchr]param0_ptr +[dist4]uint8_array // num_bytes +[u4]1 // num_elements +[u1]0 +[anchr]uint8_array diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_resp_mthd0_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_resp_mthd0_good.expected new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_resp_mthd0_good.expected @@ -0,0 +1 @@ +PASS diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_resp_mthd0_unexpected_array_header.data b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_resp_mthd0_unexpected_array_header.data new file mode 100644 index 0000000..bdcfc46 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_resp_mthd0_unexpected_array_header.data @@ -0,0 +1,19 @@ +[dist4]message_header // num_bytes +[u4]1 // version +[u4]0 // interface ID +[u4]0 // name +[u4]2 // flags kMessageIsResponse +[u4]0 // padding +[u8]1 // request_id +[anchr]message_header + +[dist4]method0_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[anchr]method0_params + +[anchr]param0_ptr +[dist4]uint8_array // num_bytes +[u4]2 // num_elements: The size is too small to hold 2 elements. +[u1]0 +[anchr]uint8_array diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_resp_mthd0_unexpected_array_header.expected b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_resp_mthd0_unexpected_array_header.expected new file mode 100644 index 0000000..5a1ec4e --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_resp_mthd0_unexpected_array_header.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_rqst_mthd0_good.data b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_rqst_mthd0_good.data new file mode 100644 index 0000000..a1fe69d --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_rqst_mthd0_good.data @@ -0,0 +1,20 @@ +[dist4]message_header // num_bytes +[u4]1 // version +[u4]0 // interface ID +[u4]0 // name +[u4]1 // flags kMessageExpectsResponse +[u4]0 // padding +[u8]7 // request_id +[anchr]message_header + +[dist4]method0_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[anchr]method0_params + +[anchr]param0_ptr +[dist4]basic_struct // num_bytes +[u4]0 // version +[s4]-1 // a +[u4]0 // padding +[anchr]basic_struct diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_rqst_mthd0_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_rqst_mthd0_good.expected new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_rqst_mthd0_good.expected @@ -0,0 +1 @@ +PASS diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_rqst_mthd0_unexpected_struct_header.data b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_rqst_mthd0_unexpected_struct_header.data new file mode 100644 index 0000000..e689adb --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_rqst_mthd0_unexpected_struct_header.data @@ -0,0 +1,17 @@ +[dist4]message_header // num_bytes +[u4]1 // version +[u4]0 // interface ID +[u4]0 // name +[u4]1 // flags kMessageExpectsResponse +[u4]0 // padding +[u8]7 // request_id +[anchr]message_header + +[dist4]method0_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[anchr]method0_params + +[anchr]param0_ptr +[u4]0 // num_bytes: The struct size is too small. +[u4]0 // version diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_rqst_mthd0_unexpected_struct_header.expected b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_rqst_mthd0_unexpected_struct_header.expected new file mode 100644 index 0000000..25aceee --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_rqst_mthd0_unexpected_struct_header.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_msghdr_invalid_flags.data b/mojo/public/interfaces/bindings/tests/data/validation/integration_msghdr_invalid_flags.data new file mode 100644 index 0000000..7198afa --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_msghdr_invalid_flags.data @@ -0,0 +1,8 @@ +[dist4]message_header // num_bytes +[u4]1 // version +[u4]0 // interface ID +[u4]0xffffffff // name +[u4]3 // flags: This combination is illegal. +[u4]0 // padding +[u8]1 // request_id +[anchr]message_header diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_msghdr_invalid_flags.expected b/mojo/public/interfaces/bindings/tests/data/validation/integration_msghdr_invalid_flags.expected new file mode 100644 index 0000000..c33fde3 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_msghdr_invalid_flags.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS diff --git a/mojo/public/interfaces/bindings/tests/data/validation/resp_boundscheck_msghdr_no_such_method.data b/mojo/public/interfaces/bindings/tests/data/validation/resp_boundscheck_msghdr_no_such_method.data new file mode 100644 index 0000000..7283509 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/resp_boundscheck_msghdr_no_such_method.data @@ -0,0 +1,8 @@ +[dist4]message_header // num_bytes +[u4]1 // version +[u4]0 // interface ID +[u4]1 // name: Method1 does not have a response message. +[u4]2 // flags: kMessageIsResponse +[u4]0 // padding +[u8]1 // request_id +[anchr]message_header diff --git a/mojo/public/interfaces/bindings/tests/data/validation/resp_boundscheck_msghdr_no_such_method.expected b/mojo/public/interfaces/bindings/tests/data/validation/resp_boundscheck_msghdr_no_such_method.expected new file mode 100644 index 0000000..65a48b3 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/resp_boundscheck_msghdr_no_such_method.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD
\ No newline at end of file diff --git a/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags1.data b/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags1.data new file mode 100644 index 0000000..d1d8d3f --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags1.data @@ -0,0 +1,8 @@ +[dist4]message_header // num_bytes +[u4]1 // version +[u4]0 // interface ID +[u4]12 // name +[u4]0 // flags: kMessageIsResponse is not set in a response. +[u4]0 // padding +[u8]1 // request_id +[anchr]message_header diff --git a/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags1.expected b/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags1.expected new file mode 100644 index 0000000..c33fde3 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags1.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS diff --git a/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags2.data b/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags2.data new file mode 100644 index 0000000..091b68c --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags2.data @@ -0,0 +1,8 @@ +[dist4]message_header // num_bytes +[u4]1 // version +[u4]0 // interface ID +[u4]12 // name +[u4]1 // flags: kMessageExpectsResponse is set in a response. +[u4]0 // padding +[u8]1 // request_id +[anchr]message_header diff --git a/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags2.expected b/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags2.expected new file mode 100644 index 0000000..c33fde3 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags2.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS diff --git a/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_no_such_method.data b/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_no_such_method.data new file mode 100644 index 0000000..0eda287 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_no_such_method.data @@ -0,0 +1,8 @@ +[dist4]message_header // num_bytes +[u4]1 // version +[u4]0 // interface ID +[u4]11 // name: Method11 does not have a response message. +[u4]2 // flags: kMessageIsResponse +[u4]0 // padding +[u8]1 // request_id +[anchr]message_header diff --git a/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_no_such_method.expected b/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_no_such_method.expected new file mode 100644 index 0000000..65a48b3 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_no_such_method.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD
\ No newline at end of file diff --git a/mojo/public/interfaces/bindings/tests/math_calculator.mojom b/mojo/public/interfaces/bindings/tests/math_calculator.mojom new file mode 100644 index 0000000..7d1b171 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/math_calculator.mojom @@ -0,0 +1,12 @@ +// 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. + +[JavaPackage="org.chromium.mojo.bindings.test.mojom.math"] +module math; + +interface Calculator { + Clear@0() => (double value@0); + Add@1(double value@0) => (double value@0); + Multiply@2(double value@0) => (double value@0); +}; diff --git a/mojo/public/interfaces/bindings/tests/no_module.mojom b/mojo/public/interfaces/bindings/tests/no_module.mojom new file mode 100644 index 0000000..f380011 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/no_module.mojom @@ -0,0 +1,9 @@ +// 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. + +// Entities without module + +enum EnumWithoutModule { + A +}; diff --git a/mojo/public/interfaces/bindings/tests/ping_service.mojom b/mojo/public/interfaces/bindings/tests/ping_service.mojom new file mode 100644 index 0000000..3e1f1c4 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/ping_service.mojom @@ -0,0 +1,10 @@ +// Copyright 2015 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. + +[JavaPackage="org.chromium.mojo.bindings.test.mojom.ping"] +module mojo.test; + +interface PingService { + Ping() => (); +}; diff --git a/mojo/public/interfaces/bindings/tests/rect.mojom b/mojo/public/interfaces/bindings/tests/rect.mojom new file mode 100644 index 0000000..b8cc977 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/rect.mojom @@ -0,0 +1,13 @@ +// 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. + +[JavaPackage="org.chromium.mojo.bindings.test.mojom.test_structs"] +module mojo.test; + +struct Rect { + int32 x; + int32 y; + int32 width; + int32 height; +}; diff --git a/mojo/public/interfaces/bindings/tests/regression_tests.mojom b/mojo/public/interfaces/bindings/tests/regression_tests.mojom new file mode 100644 index 0000000..313f1f4 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/regression_tests.mojom @@ -0,0 +1,54 @@ +// 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. + +// Module containing entities for regression tests of the generator. Entities +// must never be modified, instead new entity must be added to add new tests. +[JavaPackage="org.chromium.mojo.bindings.test.mojom.regression_tests"] +module regression_tests; + +interface CheckMethodWithEmptyResponse { +WithouParameterAndEmptyResponse() => (); +WithParameterAndEmptyResponse(bool b) => (); +}; + +interface CheckNameCollision { +WithNameCollision(bool message, bool response) => (bool message, bool response); +}; + +enum EnumWithReference { + k_STEREO_AND_KEYBOARD_MIC = 30, + k_MAX = k_STEREO_AND_KEYBOARD_MIC +}; + +enum EnumWithLowercase { + PlanarF16, + PlanarF32 +}; + +enum EnumWithNumbers { + k_2_1 = 4 +}; + +enum EnumWithK { + K = 0 +}; + +struct Edge { + Vertex? v; +}; + +struct Vertex { + EmptyStruct? e; +}; + +struct EmptyStruct { +}; + +struct A { + B? b; +}; + +struct B { + A? a; +}; diff --git a/mojo/public/interfaces/bindings/tests/sample_factory.mojom b/mojo/public/interfaces/bindings/tests/sample_factory.mojom new file mode 100644 index 0000000..ade3bf3 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/sample_factory.mojom @@ -0,0 +1,41 @@ +// 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. + +[JavaPackage="org.chromium.mojo.bindings.test.mojom.sample"] +module sample; + +import "sample_import.mojom"; + +// This sample shows how handles to MessagePipes can be sent as both parameters +// to methods as well as fields on structs. + +struct Request { + int32 x; + handle<message_pipe>? pipe; + array<handle<message_pipe>>? more_pipes; + + // Interfaces can be used as members. + imported.ImportedInterface? obj; +}; + +struct Response { + int32 x; + handle<message_pipe>? pipe; +}; + +interface NamedObject { + SetName(string name); + GetName() => (string name); +}; + +interface Factory { + DoStuff(Request request, handle<message_pipe>? pipe) => + (Response response, string text); + DoStuff2(handle<data_pipe_consumer> pipe) => (string text); + CreateNamedObject(NamedObject& obj); + RequestImportedInterface( + imported.ImportedInterface& obj) => (imported.ImportedInterface& obj); + TakeImportedInterface( + imported.ImportedInterface obj) => (imported.ImportedInterface obj); +}; diff --git a/mojo/public/interfaces/bindings/tests/sample_import.mojom b/mojo/public/interfaces/bindings/tests/sample_import.mojom new file mode 100644 index 0000000..d28cb7b --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/sample_import.mojom @@ -0,0 +1,38 @@ +// 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. + +[JavaPackage="org.chromium.mojo.bindings.test.mojom.imported"] +module imported; + +// This sample just defines some types that are imported into +// sample_service.mojom, to show how import works. + +enum Shape { + RECTANGLE = 1, + CIRCLE, + TRIANGLE, + LAST = TRIANGLE, +}; + +// These enum values should not interfere with those of Shape above. +enum AnotherShape { + RECTANGLE = 10, + CIRCLE, + TRIANGLE, +}; + +enum YetAnotherShape { + RECTANGLE = 20, + CIRCLE, + TRIANGLE, +}; + +struct Point { + int32 x; + int32 y; +}; + +interface ImportedInterface { + DoSomething(); +}; diff --git a/mojo/public/interfaces/bindings/tests/sample_import2.mojom b/mojo/public/interfaces/bindings/tests/sample_import2.mojom new file mode 100644 index 0000000..29065fc --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/sample_import2.mojom @@ -0,0 +1,28 @@ +// 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. + +[JavaPackage="org.chromium.mojo.bindings.test.mojom.imported"] +module imported; + +import "sample_import.mojom"; + +// This sample adds more types and constants to the "imported" namespace, +// to test a bug with importing multiple modules with the same namespace. + +enum Color { + RED, + BLACK, +}; + +struct Size { + int32 width; + int32 height; +}; + +struct Thing { + imported.Shape shape = RECTANGLE; + int32 color = Color.BLACK; + Point location; + Size size; +}; diff --git a/mojo/public/interfaces/bindings/tests/sample_interfaces.mojom b/mojo/public/interfaces/bindings/tests/sample_interfaces.mojom new file mode 100644 index 0000000..a2dd73f --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/sample_interfaces.mojom @@ -0,0 +1,28 @@ +// 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. + +[JavaPackage="org.chromium.mojo.bindings.test.mojom.sample", + JavaConstantsClassName="InterfaceConstants", + Foo = "hello world"] +module sample; + +const uint64 kLong = 4405; + +enum Enum { + VALUE +}; + +interface Provider { + EchoString(string a) => (string a); + EchoStrings(string a, string b) => (string a, string b); + EchoMessagePipeHandle(handle<message_pipe> a) => (handle<message_pipe> a); + EchoEnum(Enum a) => (Enum a); + EchoInt(int32 a) => (int32 a); +}; + +interface IntegerAccessor { + GetInteger() => (int64 data, [MinVersion=2] Enum type); + [MinVersion=1] + SetInteger(int64 data, [MinVersion=3] Enum type); +}; diff --git a/mojo/public/interfaces/bindings/tests/sample_service.mojom b/mojo/public/interfaces/bindings/tests/sample_service.mojom new file mode 100644 index 0000000..761cb91 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/sample_service.mojom @@ -0,0 +1,112 @@ + +// 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. + +[JavaPackage="org.chromium.mojo.bindings.test.mojom.sample"] +module sample; + +import "sample_import.mojom"; +import "sample_import2.mojom"; + +const uint8 kTwelve = 12; + +struct Bar { + enum Type { + VERTICAL = 1, + HORIZONTAL, + BOTH, + INVALID + }; + uint8 alpha@0 = 0xff; + uint8 beta@1; + uint8 gamma@2; + Type type@3 = sample.Bar.Type.VERTICAL; +}; + +struct Foo { + const string kFooby = "Fooby"; + string name@8 = kFooby; + int32 x@0; + int32 y@1; + bool a@2 = true; + bool b@3; + bool c@4; + Bar? bar@5; + array<Bar>? extra_bars@7; + array<uint8>? data@6; + handle<message_pipe>? source@9; + array<handle<data_pipe_consumer>>? input_streams@10; + array<handle<data_pipe_producer>>? output_streams@11; + array<array<bool>>? array_of_array_of_bools@12; + array<array<array<string>>>? multi_array_of_strings@13; + array<bool>? array_of_bools@14; +}; + +struct DefaultsTest { + int8 a0@0 = -12; + uint8 a1@1 = sample.kTwelve; + int16 a2@2 = 1234; + uint16 a3@3 = 34567; + int32 a4@4 = 123456; + uint32 a5@5 = 3456789012; + int64 a6@6 = -111111111111; + uint64 a7@7 = 9999999999999999999; + int32 a8@8 = 0x12345; + int32 a9@9 = -0x12345; + int32 a10@10 = +1234; + bool a11@11 = true; + bool a12@12 = false; + float a13@13 = 123.25; + double a14@14 = 1234567890.123; + double a15@15 = 1E10; + double a16@16 = -1.2E+20; + double a17@17 = +1.23E-20; + + // TODO(vtl): Add tests for default vs null when those are implemented (for + // structs, arrays, and strings). + array<uint8> a18@18; + string a19@19; + + Bar.Type a20@20 = BOTH; + imported.Point a21@21; + imported.Thing a22@22 = default; + + uint64 a23@23 = 0xFFFFFFFFFFFFFFFF; + int64 a24@24 = 0x123456789; + int64 a25@25 = -0x123456789; + + double a26@26 = double.INFINITY; + double a27@27 = double.NEGATIVE_INFINITY; + double a28@28 = double.NAN; + float a29@29 = float.INFINITY; + float a30@30 = float.NEGATIVE_INFINITY; + float a31@31 = float.NAN; +}; + +struct StructWithHoleV1 { + int32 v1 = 1; + int64 v2 = 2; +}; + +struct StructWithHoleV2 { + int32 v1 = 1; + int64 v2 = 2; + int32 v3 = 3; +}; + +interface Service { + enum BazOptions { + REGULAR = 0, + EXTRA + }; + const uint8 kFavoriteBaz = 1; + Frobinate@0(Foo? foo@0, BazOptions baz@1, Port? port@2) => (int32 result@0); + GetPort@1(Port& port @0); +}; + +// This interface is referenced above where it is defined. It also refers to +// itself from a method. +interface Port { + PostMessageToPort@0(string message_text@0, Port port@1); +}; diff --git a/mojo/public/interfaces/bindings/tests/scoping.mojom b/mojo/public/interfaces/bindings/tests/scoping.mojom new file mode 100644 index 0000000..2e9edb1 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/scoping.mojom @@ -0,0 +1,17 @@ +// Copyright 2015 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. + +module mojo.test; + +interface A { + GetB(B& b); +}; + +interface B { + GetC(C& c); +}; + +interface C { + D(); +}; diff --git a/mojo/public/interfaces/bindings/tests/serialization_test_structs.mojom b/mojo/public/interfaces/bindings/tests/serialization_test_structs.mojom new file mode 100644 index 0000000..1239e16 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/serialization_test_structs.mojom @@ -0,0 +1,36 @@ +// 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. + +[JavaPackage="org.chromium.mojo.bindings.test.mojom.mojo"] +module mojo.test; + +struct Struct1 { + uint8 i; +}; + +struct Struct2 { + handle hdl; +}; + +struct Struct3 { + Struct1 struct_1; +}; + +struct Struct4 { + array<Struct1> data; +}; + +struct Struct5 { + array<Struct1, 2> pair; +}; + +struct Struct6 { + string str; +}; + +struct StructOfNullables { + handle? hdl; + Struct1? struct_1; + string? str; +}; diff --git a/mojo/public/interfaces/bindings/tests/test_associated_interfaces.mojom b/mojo/public/interfaces/bindings/tests/test_associated_interfaces.mojom new file mode 100644 index 0000000..d81a919 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/test_associated_interfaces.mojom @@ -0,0 +1,30 @@ +// Copyright 2015 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. + +module mojo.test; + +interface FooInterface {}; + +struct StructContainsAssociated { + associated FooInterface foo_interface; + associated FooInterface& foo_request; +}; + +union UnionContainsAssociated { + associated FooInterface foo_interface; + associated FooInterface& foo_request; +}; + +interface InterfacePassesAssociated { + PassFoo(associated FooInterface foo_interface, + associated FooInterface& foo_request) => + (associated FooInterface foo_interface, + associated FooInterface& foo_request); + + PassStruct(StructContainsAssociated foo_struct) => + (StructContainsAssociated foo_struct); + + PassUnion(UnionContainsAssociated foo_union) => + (UnionContainsAssociated foo_union); +}; diff --git a/mojo/public/interfaces/bindings/tests/test_constants.mojom b/mojo/public/interfaces/bindings/tests/test_constants.mojom new file mode 100644 index 0000000..462d512 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/test_constants.mojom @@ -0,0 +1,53 @@ +// Copyright 2015 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. + +[JavaPackage="org.chromium.mojo.bindings.test.mojom.test_constants"] +module mojo.test; + +// Integral types. +const bool kBoolValue = true; + +const int8 kInt8Value = -2; + +// In the range of (MAX_INT8, MAX_UINT8]. +const uint8 kUint8Value = 128; + +// In the range of [MIN_INT16, MIN_INT8). +const int16 kInt16Value = -233; + +// In the range of (MAX_INT16, MAX_UINT16]. +const uint16 kUint16Value = 44204; + +// In the range of [MIN_INT32, MIN_INT16). +const int32 kInt32Value = -44204; + +// In the range of (MAX_INT32, MAX_UINT32]. +const uint32 kUint32Value = 4294967295; + +// In the range of [MIN_INT64, MIN_INT32). +const int64 kInt64Value = -9223372036854775807; + +// In the range of (MAX_INT64, MAX_UINT64]. +const uint64 kUint64Value = 9999999999999999999; + +// Floating point types. +const double kDoubleValue = 3.14159; +const double kDoubleInfinity = double.INFINITY; +const double kDoubleNegativeInfinity = double.NEGATIVE_INFINITY; +const double kDoubleNaN = double.NAN; + +const float kFloatValue = 2.71828; +const float kFloatInfinity = float.INFINITY; +const float kFloatNegativeInfinity = float.NEGATIVE_INFINITY; +const float kFloatNaN = float.NAN; + +struct StructWithConstants { + const int8 kInt8Value = 5; + const float kFloatValue = 765.432; +}; + +interface InterfaceWithConstants { + const uint32 kUint32Value = 20100722; + const double kDoubleValue = 12.34567; +}; diff --git a/mojo/public/interfaces/bindings/tests/test_structs.mojom b/mojo/public/interfaces/bindings/tests/test_structs.mojom new file mode 100644 index 0000000..b62df5b --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/test_structs.mojom @@ -0,0 +1,377 @@ +// 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. + +[JavaPackage="org.chromium.mojo.bindings.test.mojom.test_structs"] +module mojo.test; + +import "mojo/public/interfaces/bindings/tests/rect.mojom"; + +struct NamedRegion { + string? name; + array<Rect>? rects; +}; + +struct RectPair { + Rect? first; + Rect? second; +}; + +struct EmptyStruct { +}; + +// Used to verify that struct fields which don't specify a default are +// initialized to: false for bool, 0 for numbers, and null for strings, +// handles, and structs. The "?" nullable suffix shouldn't have any +// impact on initial field values. + +struct NoDefaultFieldValues { + bool f0; + int8 f1; + uint8 f2; + int16 f3; + uint16 f4; + int32 f5; + uint32 f6; + int64 f7; + uint64 f8; + float f9; + double f10; + string f11; + string? f12; + handle<message_pipe> f13; + handle<data_pipe_consumer> f14; + handle<data_pipe_producer> f15; + handle<message_pipe>? f16; + handle<data_pipe_consumer>? f17; + handle<data_pipe_producer>? f18; + handle f19; + handle? f20; + handle<shared_buffer> f21; + handle<shared_buffer>? f22; + array<string> f23; + array<string?> f24; + array<string>? f25; + array<string?>? f26; + EmptyStruct f27; + EmptyStruct? f28; +}; + +// Used to verify that struct fields with an explicit default value +// are initialized correctly. The "?" nullable suffix shouldn't have any +// impact on initial field values. + +struct DefaultFieldValues { + const string kFoo = "foo"; + bool f0 = true; + int8 f1 = 100; + uint8 f2 = 100; + int16 f3 = 100; + uint16 f4 = 100; + int32 f5 = 100; + uint32 f6 = 100; + int64 f7 = 100; + uint64 f8 = 100; + float f9 = 100; + float f10 = 100.0; + double f11 = 100; + double f12 = 100.0; + string f13 = kFoo; + string? f14 = kFoo; + Rect f15 = default; + Rect? f16 = default; +}; + +// Used to verify that the code generated for enum and const values defined +// within a struct is correct. Assuming that a constant's value can be a literal +// or another constant and that enum values can either be an integer constant or +// another value from the same enum type. + +struct ScopedConstants { + const int32 TEN = 10; + const int32 ALSO_TEN = TEN; + enum EType { + E0, + E1, + E2 = 10, + E3 = E2, + E4, + }; + const int32 TEN_TOO = EType.E2; + EType f0 = E0; // 0 + EType f1 = E1; // 1 + EType f2 = E2; // 10 + EType f3 = E3; // 10 + EType f4 = E4; // 11 + int32 f5 = TEN; + int32 f6 = ALSO_TEN; +}; + +// Used to verify that all possible Map key field types can be encoded and +// decoded successfully. + +struct MapKeyTypes { + map<bool, bool> f0; + map<int8, int8> f1; + map<uint8, uint8> f2; + map<int16, int16> f3; + map<uint16, uint16> f4; + map<int32, int32> f5; + map<uint32, uint32> f6; + map<int64, int64> f7; + map<uint64, uint64> f8; + map<float, float> f9; + map<double, double> f10; + map<string, string> f11; +}; + +// Used to verify that various map value types can be encoded and decoded +// successfully. + +struct MapValueTypes { + map<string, array<string>> f0; + map<string, array<string>?> f1; + map<string, array<string?>> f2; + map<string, array<string, 2>> f3; + map<string, array<array<string, 2>?>> f4; + map<string, array<array<string, 2>, 1>> f5; + map<string, Rect?> f6; + map<string, map<string, string>> f7; + map<string, array<map<string, string>>> f8; + map<string, handle> f9; + map<string, array<handle>> f10; + map<string, map<string, handle>> f11; +}; + +// Used to verify that various array types can be encoded and decoded +// successfully. + +struct ArrayValueTypes { + array<int8> f0; + array<int16> f1; + array<int32> f2; + array<int64> f3; + array<float> f4; + array<double> f5; +}; + +// Used to verify that various float and double values can be encoded and +// decoded correctly. + +struct FloatNumberValues { + const double V0 = double.INFINITY; + const double V1 = double.NEGATIVE_INFINITY; + const double V2 = double.NAN; + const float V3 = float.INFINITY; + const float V4 = float.NEGATIVE_INFINITY; + const float V5 = float.NAN; + const float V6 = 0; + const double V7 = 1234567890.123; + const double V8 = 1.2E+20; + const double V9 = -1.2E+20; + + double f0 = V0; + double f1 = V1; + double f2 = V2; + float f3 = V3; + float f4 = V4; + float f5 = V5; + float f6 = V6; + double f7 = V7; + double f8 = V8; + double f9 = V9; +}; + +// Used to verify that various signed integer values can be encoded and +// decoded correctly. + +struct IntegerNumberValues { + const int8 V0 = -128; // Minimum + const int8 V1 = -1; // -1 + const int8 V2 = 0; // 0 + const int8 V3 = 42; // An arbitrary valid value. + const int8 V4 = 127; // Maximum + + const int16 V5 = -32768; // ... + const int16 V6 = -1; + const int16 V7 = 0; + const int16 V8 = 12345; + const int16 V9 = 32767; + + const int32 V10 = -2147483648; + const int32 V11 = -1; + const int32 V12 = 0; + const int32 V13 = 1234567890; + const int32 V14 = 2147483647; + + // The limits for JavaScript integers are +/- (2^53 - 1). + const int64 V15 = -9007199254740991; // Number.MIN_SAFE_INTEGER + const int64 V16 = -1; + const int64 V17 = 0; + const int64 V18 = 1234567890123456; + const int64 V19 = 9007199254740991; // Number.MAX_SAFE_INTEGER + + int8 f0 = V0; + int8 f1 = V1; + int8 f2 = V2; + int8 f3 = V3; + int8 f4 = V4; + + int16 f5 = V5; + int16 f6 = V6; + int16 f7 = V7; + int16 f8 = V8; + int16 f9 = V9; + + int32 f10 = V10; + int32 f11 = V11; + int32 f12 = V12; + int32 f13 = V13; + int32 f14 = V14; + + int64 f15 = V15; + int64 f16 = V16; + int64 f17 = V17; + int64 f18 = V18; + int64 f19 = V19; +}; + +// Used to verify that various unsigned integer values can be encoded and +// decoded correctly. + +struct UnsignedNumberValues { + const uint8 V0 = 0; // Minimum = 0. + const uint8 V1 = 42; // An arbitrary valid value. + const uint8 V2 = 0xFF; // Maximum + + const uint16 V3 = 0; // ... + const uint16 V4 = 12345; + const uint16 V5 = 0xFFFF; + + const uint32 V6 = 0; + const uint32 V7 = 1234567890; + const uint32 V8 = 0xFFFFFFFF; + + // The limits for JavaScript integers are +/- (2^53 - 1). + const uint64 V9 = 0; + const uint64 V10 = 1234567890123456; + const uint64 V11 = 9007199254740991; // Number.MAX_SAFE_INTEGER + + uint8 f0 = V0; + uint8 f1 = V1; + uint8 f2 = V2; + + uint16 f3 = V3; + uint16 f4 = V4; + uint16 f5 = V5; + + uint32 f6 = V6; + uint32 f7 = V7; + uint32 f8 = V8; + + uint64 f9 = V9; + uint64 f10 = V10; + uint64 f11 = V11; +}; + +// Used to verify that various (packed) boolean array values can be encoded +// and decoded correctly. + +struct BitArrayValues { + array<bool, 1> f0; + array<bool, 7> f1; + array<bool, 9> f2; + array<bool> f3; + array<array<bool>> f4; + array<array<bool>?> f5; + array<array<bool, 2>?> f6; +}; + +// Used to verify that different versions can be decoded correctly. + +struct MultiVersionStruct { + [MinVersion=0] + int32 f_int32; + [MinVersion=1] + Rect? f_rect; + [MinVersion=3] + string? f_string; + [MinVersion=5] + array<int8>? f_array; + [MinVersion=7] + handle<message_pipe>? f_message_pipe; + [MinVersion=7] + bool f_bool; + [MinVersion=9] + int16 f_int16; +}; + +struct MultiVersionStructV0 { + [MinVersion=0] + int32 f_int32; +}; + +struct MultiVersionStructV1 { + [MinVersion=0] + int32 f_int32; + [MinVersion=1] + Rect? f_rect; +}; + +struct MultiVersionStructV3 { + [MinVersion=0] + int32 f_int32; + [MinVersion=1] + Rect? f_rect; + [MinVersion=3] + string? f_string; +}; + +struct MultiVersionStructV5 { + [MinVersion=0] + int32 f_int32; + [MinVersion=1] + Rect? f_rect; + [MinVersion=3] + string? f_string; + [MinVersion=5] + array<int8>? f_array; +}; + +struct MultiVersionStructV7 { + [MinVersion=0] + int32 f_int32; + [MinVersion=1] + Rect? f_rect; + [MinVersion=3] + string? f_string; + [MinVersion=5] + array<int8>? f_array; + [MinVersion=7] + handle<message_pipe>? f_message_pipe; + [MinVersion=7] + bool f_bool; +}; + +// Used to verify that interfaces that are struct members can be defined in the +// same file. + +interface SomeInterface { + SomeMethod(RectPair pair) => (RectPair other_pair); +}; + +struct ContainsInterface { + SomeInterface some_interface; +}; + +// Regression: Verify that a field can be called |other|. + +struct ContainsOther { + int32 other; +}; + +// Used to verify that structs can contain interface requests. + +struct ContainsInterfaceRequest { + SomeInterface& request; +}; diff --git a/mojo/public/interfaces/bindings/tests/test_unions.mojom b/mojo/public/interfaces/bindings/tests/test_unions.mojom new file mode 100644 index 0000000..e998f47 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/test_unions.mojom @@ -0,0 +1,96 @@ +// 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. + +module mojo.test; + +enum AnEnum { + FIRST, SECOND +}; + +union PodUnion { + int8 f_int8; + int8 f_int8_other; + uint8 f_uint8; + int16 f_int16; + uint16 f_uint16; + int32 f_int32; + uint32 f_uint32; + int64 f_int64; + uint64 f_uint64; + float f_float; + double f_double; + bool f_bool; + AnEnum f_enum; +}; + +union ObjectUnion { + int8 f_int8; + string f_string; + DummyStruct f_dummy; + DummyStruct? f_nullable; + array<int8> f_array_int8; + map<string, int8> f_map_int8; + PodUnion f_pod_union; +}; + +union HandleUnion { + handle f_handle; + handle<message_pipe> f_message_pipe; + handle<data_pipe_consumer> f_data_pipe_consumer; + handle<data_pipe_producer> f_data_pipe_producer; + handle<shared_buffer> f_shared_buffer; + SmallCache f_small_cache; +}; + +struct WrapperStruct { + ObjectUnion? object_union; + PodUnion? pod_union; + HandleUnion? handle_union; +}; + +struct DummyStruct { + int8 f_int8; +}; + +struct SmallStruct { + DummyStruct? dummy_struct; + PodUnion? pod_union; + array<PodUnion>? pod_union_array; + array<PodUnion?>? nullable_pod_union_array; + array<DummyStruct>? s_array; + map<string, PodUnion>? pod_union_map; + map<string, PodUnion?>? nullable_pod_union_map; +}; + +struct SmallStructNonNullableUnion { + PodUnion pod_union; +}; + +struct SmallObjStruct { + ObjectUnion obj_union; + int8 f_int8; +}; + +interface SmallCache { + SetIntValue(int64 int_value); + GetIntValue() => (int64 int_value); +}; + +interface UnionInterface { + Echo(PodUnion in_val) => (PodUnion out_val); +}; + +struct TryNonNullStruct { + DummyStruct? nullable; + DummyStruct non_nullable; +}; + +union OldUnion { + int8 f_int8; +}; + +union NewUnion { + int8 f_int8; + int16 f_int16; +}; diff --git a/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom b/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom new file mode 100644 index 0000000..dd2eb4c --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom @@ -0,0 +1,97 @@ +// 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. + + +[JavaPackage="org.chromium.mojo.bindings.test.mojom.mojo"] +module mojo.test; + +struct StructA { + uint64 i; +}; + +struct StructB { + StructA struct_a; +}; + +struct StructC { + array<uint8> data; +}; + +struct StructD { + array<handle<message_pipe>> message_pipes; +}; + +struct StructE { + StructD struct_d; + handle<data_pipe_consumer> data_pipe_consumer; +}; + +struct StructF { + array<uint8, 3> fixed_size_array; +}; + +struct StructG { + int32 i; + [MinVersion=1] + StructA? struct_a; + [MinVersion=3] + string? str; + [MinVersion=3] + bool b; +}; + +interface InterfaceA { +}; + +// This interface is used for testing bounds-checking in the mojom +// binding code. If you add a method please update the files +// ./data/validation/boundscheck_*. If you add a response please update +// ./data/validation/resp_boundscheck_*. +interface BoundsCheckTestInterface { + Method0(uint8 param0) => (uint8 param0); + Method1(uint8 param0); +}; + +interface ConformanceTestInterface { + Method0(float param0); + Method1(StructA param0); + Method2(StructB param0, StructA param1); + Method3(array<bool> param0); + Method4(StructC param0, array<uint8> param1); + Method5(StructE param0, handle<data_pipe_producer> param1); + Method6(array<array<uint8>> param0); + Method7(StructF param0, array<array<uint8, 3>?, 2> param1); + Method8(array<array<string>?> param0); + Method9(array<array<handle?>>? param0); + Method10(map<string, uint8> param0); + Method11(StructG param0); + Method12(float param0) => (float param0); + Method13(InterfaceA? param0, uint32 param1, InterfaceA? param2); +}; + +struct BasicStruct { + int32 a; +}; + +interface IntegrationTestInterface { + Method0(BasicStruct param0) => (array<uint8> param0); +}; + +// An enum generates a enum-value validation function, so we want to test it. +// E.g., valid enum values for this enum should be: -3, 0, 1, 10 +enum BasicEnum { + A, + B, + C = A, + D = -3, + E = 0xA +}; + +// The enum validation function should be generated within the scope of this +// struct. +struct StructWithEnum { + enum EnumWithin { + A, B, C, D + }; +}; diff --git a/mojo/public/interfaces/bindings/tests/versioning_test_client.mojom b/mojo/public/interfaces/bindings/tests/versioning_test_client.mojom new file mode 100644 index 0000000..f0136db --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/versioning_test_client.mojom @@ -0,0 +1,34 @@ +// Copyright 2015 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. + +module mojo.test.versioning; + +// versioning_test_service.mojom and versioning_test_client.mojom contain +// different versions of Mojom definitions for a fictitious human resource +// management system. They are used to test the versioning mechanism. + +enum Department { + SALES, + DEV +}; + +struct Employee { + uint64 employee_id; + string name; + Department department; +}; + +interface HumanResourceDatabase { + AddEmployee(Employee employee) => (bool success); + + QueryEmployee(uint64 id, [MinVersion=1] bool retrieve_finger_print) + => (Employee? employee, [MinVersion=1] array<uint8>? finger_print); + + [MinVersion=1] + AttachFingerPrint(uint64 id, array<uint8> finger_print) + => (bool success); + + [MinVersion=2] + ListEmployeeIds() => (array<uint64>? ids); +}; diff --git a/mojo/public/interfaces/bindings/tests/versioning_test_service.mojom b/mojo/public/interfaces/bindings/tests/versioning_test_service.mojom new file mode 100644 index 0000000..59b3832 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/versioning_test_service.mojom @@ -0,0 +1,38 @@ +// Copyright 2015 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. + +module mojo.test.versioning; + +// versioning_test_service.mojom and versioning_test_client.mojom contain +// different versions of Mojom definitions for a fictitious human resource +// management system. They are used to test the versioning mechanism. + +enum Department { + SALES, + DEV +}; + +struct Date { + uint16 year; + uint8 month; + uint8 day; +}; + +struct Employee { + uint64 employee_id; + string name; + Department department; + [MinVersion=1] Date? birthday; +}; + +interface HumanResourceDatabase { + AddEmployee(Employee employee) => (bool success); + + QueryEmployee(uint64 id, [MinVersion=1] bool retrieve_finger_print) + => (Employee? employee, [MinVersion=1] array<uint8>? finger_print); + + [MinVersion=1] + AttachFingerPrint(uint64 id, array<uint8> finger_print) + => (bool success); +}; diff --git a/mojo/public/java/BUILD.gn b/mojo/public/java/BUILD.gn new file mode 100644 index 0000000..d543394 --- /dev/null +++ b/mojo/public/java/BUILD.gn @@ -0,0 +1,61 @@ +# 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. + +import("//build/config/android/rules.gni") + +android_library("system") { + java_files = [ + "system/src/org/chromium/mojo/system/AsyncWaiter.java", + "system/src/org/chromium/mojo/system/Core.java", + "system/src/org/chromium/mojo/system/DataPipe.java", + "system/src/org/chromium/mojo/system/Flags.java", + "system/src/org/chromium/mojo/system/Handle.java", + "system/src/org/chromium/mojo/system/InvalidHandle.java", + "system/src/org/chromium/mojo/system/MessagePipeHandle.java", + "system/src/org/chromium/mojo/system/MojoException.java", + "system/src/org/chromium/mojo/system/MojoResult.java", + "system/src/org/chromium/mojo/system/Pair.java", + "system/src/org/chromium/mojo/system/ResultAnd.java", + "system/src/org/chromium/mojo/system/SharedBufferHandle.java", + "system/src/org/chromium/mojo/system/UntypedHandle.java", + "system/src/org/chromium/mojo/system/RunLoop.java", + ] +} + +android_library("bindings") { + java_files = [ + "bindings/src/org/chromium/mojo/bindings/AutoCloseableRouter.java", + "bindings/src/org/chromium/mojo/bindings/BindingsHelper.java", + "bindings/src/org/chromium/mojo/bindings/Callbacks.java", + "bindings/src/org/chromium/mojo/bindings/ConnectionErrorHandler.java", + "bindings/src/org/chromium/mojo/bindings/Connector.java", + "bindings/src/org/chromium/mojo/bindings/DataHeader.java", + "bindings/src/org/chromium/mojo/bindings/Decoder.java", + "bindings/src/org/chromium/mojo/bindings/DelegatingConnectionErrorHandler.java", + "bindings/src/org/chromium/mojo/bindings/DeserializationException.java", + "bindings/src/org/chromium/mojo/bindings/Encoder.java", + "bindings/src/org/chromium/mojo/bindings/ExecutorFactory.java", + "bindings/src/org/chromium/mojo/bindings/HandleOwner.java", + "bindings/src/org/chromium/mojo/bindings/InterfaceControlMessagesHelper.java", + "bindings/src/org/chromium/mojo/bindings/Interface.java", + "bindings/src/org/chromium/mojo/bindings/InterfaceRequest.java", + "bindings/src/org/chromium/mojo/bindings/MessageHeader.java", + "bindings/src/org/chromium/mojo/bindings/Message.java", + "bindings/src/org/chromium/mojo/bindings/MessageReceiver.java", + "bindings/src/org/chromium/mojo/bindings/MessageReceiverWithResponder.java", + "bindings/src/org/chromium/mojo/bindings/RouterImpl.java", + "bindings/src/org/chromium/mojo/bindings/Router.java", + "bindings/src/org/chromium/mojo/bindings/SerializationException.java", + "bindings/src/org/chromium/mojo/bindings/ServiceMessage.java", + "bindings/src/org/chromium/mojo/bindings/SideEffectFreeCloseable.java", + "bindings/src/org/chromium/mojo/bindings/Struct.java", + "bindings/src/org/chromium/mojo/bindings/Union.java", + ] + + deps = [ + ":system", + ] + + srcjar_deps = [ "../interfaces/bindings:bindings_java_sources" ] +} diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AutoCloseableRouter.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AutoCloseableRouter.java new file mode 100644 index 0000000..8a83be9 --- /dev/null +++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AutoCloseableRouter.java @@ -0,0 +1,116 @@ +// 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. + +package org.chromium.mojo.bindings; + +import org.chromium.mojo.system.Core; +import org.chromium.mojo.system.MessagePipeHandle; + +import java.util.concurrent.Executor; + +/** + * Wrapper around {@link Router} that will close the connection when not referenced anymore. + */ +class AutoCloseableRouter implements Router { + + /** + * The underlying router. + */ + private final Router mRouter; + + /** + * The executor to close the underlying router. + */ + private final Executor mExecutor; + + /** + * Flags to keep track if this router has been correctly closed. + */ + private boolean mClosed; + + /** + * Constructor. + */ + public AutoCloseableRouter(Core core, Router router) { + mRouter = router; + mExecutor = ExecutorFactory.getExecutorForCurrentThread(core); + } + + /** + * @see Router#setIncomingMessageReceiver(MessageReceiverWithResponder) + */ + @Override + public void setIncomingMessageReceiver(MessageReceiverWithResponder incomingMessageReceiver) { + mRouter.setIncomingMessageReceiver(incomingMessageReceiver); + } + + /** + * @see HandleOwner#passHandle() + */ + @Override + public MessagePipeHandle passHandle() { + return mRouter.passHandle(); + } + + /** + * @see MessageReceiver#accept(Message) + */ + @Override + public boolean accept(Message message) { + return mRouter.accept(message); + } + + /** + * @see MessageReceiverWithResponder#acceptWithResponder(Message, MessageReceiver) + */ + @Override + public boolean acceptWithResponder(Message message, MessageReceiver responder) { + return mRouter.acceptWithResponder(message, responder); + + } + + /** + * @see Router#start() + */ + @Override + public void start() { + mRouter.start(); + } + + /** + * @see Router#setErrorHandler(ConnectionErrorHandler) + */ + @Override + public void setErrorHandler(ConnectionErrorHandler errorHandler) { + mRouter.setErrorHandler(errorHandler); + } + + /** + * @see java.io.Closeable#close() + */ + @Override + public void close() { + mRouter.close(); + mClosed = true; + } + + /** + * @see Object#finalize() + */ + @Override + protected void finalize() throws Throwable { + if (!mClosed) { + mExecutor.execute(new Runnable() { + + @Override + public void run() { + close(); + } + }); + throw new IllegalStateException("Warning: Router objects should be explicitly closed " + + "when no longer required otherwise you may leak handles."); + } + super.finalize(); + } +} diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/BindingsHelper.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/BindingsHelper.java new file mode 100644 index 0000000..6146316 --- /dev/null +++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/BindingsHelper.java @@ -0,0 +1,199 @@ +// 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. + +package org.chromium.mojo.bindings; + +import org.chromium.mojo.system.AsyncWaiter; +import org.chromium.mojo.system.Handle; + +/** + * Helper functions. + */ +public class BindingsHelper { + /** + * Alignment in bytes for mojo serialization. + */ + public static final int ALIGNMENT = 8; + + /** + * The size, in bytes, of a serialized handle. A handle is serialized as an int representing the + * offset of the handle in the list of handles. + */ + public static final int SERIALIZED_HANDLE_SIZE = 4; + + /** + * The size, in bytes, of a serialized interface, which consists of a serialized handle (4 + * bytes) and a version number (4 bytes). + */ + public static final int SERIALIZED_INTERFACE_SIZE = 8; + + /** + * The size, in bytes, of a serialized pointer. A pointer is serializaed as an unsigned long + * representing the offset from its position to the pointed elemnt. + */ + public static final int POINTER_SIZE = 8; + + /** + * The size, in bytes, of a serialized union. + */ + public static final int UNION_SIZE = 16; + + /** + * The header for a serialized map element. + */ + public static final DataHeader MAP_STRUCT_HEADER = new DataHeader(24, 0); + + /** + * The value used for the expected length of a non-fixed size array. + */ + public static final int UNSPECIFIED_ARRAY_LENGTH = -1; + + /** + * Passed as |arrayNullability| when neither the array nor its elements are nullable. + */ + public static final int NOTHING_NULLABLE = 0; + + /** + * "Array bit" of |arrayNullability| is set iff the array itself is nullable. + */ + public static final int ARRAY_NULLABLE = (1 << 0); + + /** + * "Element bit" of |arrayNullability| is set iff the array elements are nullable. + */ + public static final int ELEMENT_NULLABLE = (1 << 1); + + public static boolean isArrayNullable(int arrayNullability) { + return (arrayNullability & ARRAY_NULLABLE) > 0; + } + + public static boolean isElementNullable(int arrayNullability) { + return (arrayNullability & ELEMENT_NULLABLE) > 0; + } + + /** + * Align |size| on {@link BindingsHelper#ALIGNMENT}. + */ + public static int align(int size) { + return (size + ALIGNMENT - 1) & ~(ALIGNMENT - 1); + } + + /** + * Align |size| on {@link BindingsHelper#ALIGNMENT}. + */ + public static long align(long size) { + return (size + ALIGNMENT - 1) & ~(ALIGNMENT - 1); + } + + /** + * Compute the size in bytes of the given string encoded as utf8. + */ + public static int utf8StringSizeInBytes(String s) { + int res = 0; + for (int i = 0; i < s.length(); ++i) { + char c = s.charAt(i); + int codepoint = c; + if (isSurrogate(c)) { + i++; + char c2 = s.charAt(i); + codepoint = Character.toCodePoint(c, c2); + } + res += 1; + if (codepoint > 0x7f) { + res += 1; + if (codepoint > 0x7ff) { + res += 1; + if (codepoint > 0xffff) { + res += 1; + if (codepoint > 0x1fffff) { + res += 1; + if (codepoint > 0x3ffffff) { + res += 1; + } + } + } + } + } + } + return res; + } + + /** + * Returns |true| if and only if the two objects are equals, handling |null|. + */ + public static boolean equals(Object o1, Object o2) { + if (o1 == o2) { + return true; + } + if (o1 == null) { + return false; + } + return o1.equals(o2); + } + + /** + * Returns the hash code of the object, handling |null|. + */ + public static int hashCode(Object o) { + if (o == null) { + return 0; + } + return o.hashCode(); + } + + /** + * Returns the hash code of the value. + */ + public static int hashCode(boolean o) { + return o ? 1231 : 1237; + } + + /** + * Returns the hash code of the value. + */ + public static int hashCode(long o) { + return (int) (o ^ (o >>> 32)); + } + + /** + * Returns the hash code of the value. + */ + public static int hashCode(float o) { + return Float.floatToIntBits(o); + } + + /** + * Returns the hash code of the value. + */ + public static int hashCode(double o) { + return hashCode(Double.doubleToLongBits(o)); + } + + /** + * Returns the hash code of the value. + */ + public static int hashCode(int o) { + return o; + } + + /** + * Determines if the given {@code char} value is a Unicode <i>surrogate code unit</i>. See + * {@link Character#isSurrogate}. Extracting here because the method only exists at API level + * 19. + */ + private static boolean isSurrogate(char c) { + return c >= Character.MIN_SURROGATE && c < (Character.MAX_SURROGATE + 1); + } + + /** + * Returns an {@link AsyncWaiter} to use with the given handle, or |null| if none if available. + */ + static AsyncWaiter getDefaultAsyncWaiterForHandle(Handle handle) { + if (handle.getCore() != null) { + return handle.getCore().getDefaultAsyncWaiter(); + } else { + return null; + } + } +} diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Callbacks.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Callbacks.java new file mode 100644 index 0000000..c6b14c1 --- /dev/null +++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Callbacks.java @@ -0,0 +1,130 @@ +// 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. + +// This file was generated using +// mojo/tools/generate_java_callback_interfaces.py + +package org.chromium.mojo.bindings; + +/** + * Contains a generic interface for callbacks. + */ +public interface Callbacks { + + /** + * A generic callback. + */ + interface Callback0 { + /** + * Call the callback. + */ + public void call(); + } + + /** + * A generic 1-argument callback. + * + * @param <T1> the type of argument 1. + */ + interface Callback1<T1> { + /** + * Call the callback. + */ + public void call(T1 arg1); + } + + /** + * A generic 2-argument callback. + * + * @param <T1> the type of argument 1. + * @param <T2> the type of argument 2. + */ + interface Callback2<T1, T2> { + /** + * Call the callback. + */ + public void call(T1 arg1, T2 arg2); + } + + /** + * A generic 3-argument callback. + * + * @param <T1> the type of argument 1. + * @param <T2> the type of argument 2. + * @param <T3> the type of argument 3. + */ + interface Callback3<T1, T2, T3> { + /** + * Call the callback. + */ + public void call(T1 arg1, T2 arg2, T3 arg3); + } + + /** + * A generic 4-argument callback. + * + * @param <T1> the type of argument 1. + * @param <T2> the type of argument 2. + * @param <T3> the type of argument 3. + * @param <T4> the type of argument 4. + */ + interface Callback4<T1, T2, T3, T4> { + /** + * Call the callback. + */ + public void call(T1 arg1, T2 arg2, T3 arg3, T4 arg4); + } + + /** + * A generic 5-argument callback. + * + * @param <T1> the type of argument 1. + * @param <T2> the type of argument 2. + * @param <T3> the type of argument 3. + * @param <T4> the type of argument 4. + * @param <T5> the type of argument 5. + */ + interface Callback5<T1, T2, T3, T4, T5> { + /** + * Call the callback. + */ + public void call(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5); + } + + /** + * A generic 6-argument callback. + * + * @param <T1> the type of argument 1. + * @param <T2> the type of argument 2. + * @param <T3> the type of argument 3. + * @param <T4> the type of argument 4. + * @param <T5> the type of argument 5. + * @param <T6> the type of argument 6. + */ + interface Callback6<T1, T2, T3, T4, T5, T6> { + /** + * Call the callback. + */ + public void call(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6); + } + + /** + * A generic 7-argument callback. + * + * @param <T1> the type of argument 1. + * @param <T2> the type of argument 2. + * @param <T3> the type of argument 3. + * @param <T4> the type of argument 4. + * @param <T5> the type of argument 5. + * @param <T6> the type of argument 6. + * @param <T7> the type of argument 7. + */ + interface Callback7<T1, T2, T3, T4, T5, T6, T7> { + /** + * Call the callback. + */ + public void call(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7); + } + +} diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ConnectionErrorHandler.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ConnectionErrorHandler.java new file mode 100644 index 0000000..f601fb8 --- /dev/null +++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ConnectionErrorHandler.java @@ -0,0 +1,15 @@ +// 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. + +package org.chromium.mojo.bindings; + +import org.chromium.mojo.system.MojoException; + +/** + * A {@link ConnectionErrorHandler} is notified of an error happening while using the bindings over + * message pipes. + */ +public interface ConnectionErrorHandler { + public void onConnectionError(MojoException e); +} diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Connector.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Connector.java new file mode 100644 index 0000000..3686bcf --- /dev/null +++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Connector.java @@ -0,0 +1,250 @@ +// 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. + +package org.chromium.mojo.bindings; + +import org.chromium.mojo.system.AsyncWaiter; +import org.chromium.mojo.system.Core; +import org.chromium.mojo.system.MessagePipeHandle; +import org.chromium.mojo.system.MessagePipeHandle.ReadMessageResult; +import org.chromium.mojo.system.MojoException; +import org.chromium.mojo.system.MojoResult; +import org.chromium.mojo.system.ResultAnd; + +import java.nio.ByteBuffer; + +/** + * A {@link Connector} owns a {@link MessagePipeHandle} and will send any received messages to the + * registered {@link MessageReceiver}. It also acts as a {@link MessageReceiver} and will send any + * message through the handle. + * <p> + * The method |start| must be called before the {@link Connector} will start listening to incoming + * messages. + */ +public class Connector implements MessageReceiver, HandleOwner<MessagePipeHandle> { + + /** + * The callback that is notified when the state of the owned handle changes. + */ + private final AsyncWaiterCallback mAsyncWaiterCallback = new AsyncWaiterCallback(); + + /** + * The owned message pipe. + */ + private final MessagePipeHandle mMessagePipeHandle; + + /** + * A waiter which is notified when a new message is available on the owned message pipe. + */ + private final AsyncWaiter mAsyncWaiter; + + /** + * The {@link MessageReceiver} to which received messages are sent. + */ + private MessageReceiver mIncomingMessageReceiver; + + /** + * The Cancellable for the current wait. Is |null| when not currently waiting for new messages. + */ + private AsyncWaiter.Cancellable mCancellable; + + /** + * The error handler to notify of errors. + */ + private ConnectionErrorHandler mErrorHandler; + + /** + * Create a new connector over a |messagePipeHandle|. The created connector will use the default + * {@link AsyncWaiter} from the {@link Core} implementation of |messagePipeHandle|. + */ + public Connector(MessagePipeHandle messagePipeHandle) { + this(messagePipeHandle, BindingsHelper.getDefaultAsyncWaiterForHandle(messagePipeHandle)); + } + + /** + * Create a new connector over a |messagePipeHandle| using the given {@link AsyncWaiter} to get + * notified of changes on the handle. + */ + public Connector(MessagePipeHandle messagePipeHandle, AsyncWaiter asyncWaiter) { + mCancellable = null; + mMessagePipeHandle = messagePipeHandle; + mAsyncWaiter = asyncWaiter; + } + + /** + * Set the {@link MessageReceiver} that will receive message from the owned message pipe. + */ + public void setIncomingMessageReceiver(MessageReceiver incomingMessageReceiver) { + mIncomingMessageReceiver = incomingMessageReceiver; + } + + /** + * Set the {@link ConnectionErrorHandler} that will be notified of errors on the owned message + * pipe. + */ + public void setErrorHandler(ConnectionErrorHandler errorHandler) { + mErrorHandler = errorHandler; + } + + /** + * Start listening for incoming messages. + */ + public void start() { + assert mCancellable == null; + registerAsyncWaiterForRead(); + } + + /** + * @see MessageReceiver#accept(Message) + */ + @Override + public boolean accept(Message message) { + try { + mMessagePipeHandle.writeMessage(message.getData(), + message.getHandles(), MessagePipeHandle.WriteFlags.NONE); + return true; + } catch (MojoException e) { + onError(e); + return false; + } + } + + /** + * Pass the owned handle of the connector. After this, the connector is disconnected. It cannot + * accept new message and it isn't listening to the handle anymore. + * + * @see org.chromium.mojo.bindings.HandleOwner#passHandle() + */ + @Override + public MessagePipeHandle passHandle() { + cancelIfActive(); + MessagePipeHandle handle = mMessagePipeHandle.pass(); + if (mIncomingMessageReceiver != null) { + mIncomingMessageReceiver.close(); + } + return handle; + } + + /** + * @see java.io.Closeable#close() + */ + @Override + public void close() { + cancelIfActive(); + mMessagePipeHandle.close(); + if (mIncomingMessageReceiver != null) { + MessageReceiver incomingMessageReceiver = mIncomingMessageReceiver; + mIncomingMessageReceiver = null; + incomingMessageReceiver.close(); + } + } + + private class AsyncWaiterCallback implements AsyncWaiter.Callback { + + /** + * @see org.chromium.mojo.system.AsyncWaiter.Callback#onResult(int) + */ + @Override + public void onResult(int result) { + Connector.this.onAsyncWaiterResult(result); + } + + /** + * @see org.chromium.mojo.system.AsyncWaiter.Callback#onError(MojoException) + */ + @Override + public void onError(MojoException exception) { + mCancellable = null; + Connector.this.onError(exception); + } + + } + + /** + * @see org.chromium.mojo.system.AsyncWaiter.Callback#onResult(int) + */ + private void onAsyncWaiterResult(int result) { + mCancellable = null; + if (result == MojoResult.OK) { + readOutstandingMessages(); + } else { + onError(new MojoException(result)); + } + } + + private void onError(MojoException exception) { + close(); + assert mCancellable == null; + if (mErrorHandler != null) { + mErrorHandler.onConnectionError(exception); + } + } + + /** + * Register to be called back when a new message is available on the owned message pipe. + */ + private void registerAsyncWaiterForRead() { + assert mCancellable == null; + if (mAsyncWaiter != null) { + mCancellable = mAsyncWaiter.asyncWait(mMessagePipeHandle, Core.HandleSignals.READABLE, + Core.DEADLINE_INFINITE, mAsyncWaiterCallback); + } else { + onError(new MojoException(MojoResult.INVALID_ARGUMENT)); + } + } + + /** + * Read all available messages on the owned message pipe. + */ + private void readOutstandingMessages() { + ResultAnd<Boolean> result; + do { + try { + result = readAndDispatchMessage(mMessagePipeHandle, mIncomingMessageReceiver); + } catch (MojoException e) { + onError(e); + return; + } + } while (result.getValue()); + if (result.getMojoResult() == MojoResult.SHOULD_WAIT) { + registerAsyncWaiterForRead(); + } else { + onError(new MojoException(result.getMojoResult())); + } + } + + private void cancelIfActive() { + if (mCancellable != null) { + mCancellable.cancel(); + mCancellable = null; + } + } + + /** + * Read a message, and pass it to the given |MessageReceiver| if not null. If the + * |MessageReceiver| is null, the message is lost. + * + * @param receiver The {@link MessageReceiver} that will receive the read {@link Message}. Can + * be <code>null</code>, in which case the message is discarded. + */ + static ResultAnd<Boolean> readAndDispatchMessage( + MessagePipeHandle handle, MessageReceiver receiver) { + // TODO(qsr) Allow usage of a pool of pre-allocated buffer for performance. + ResultAnd<ReadMessageResult> result = + handle.readMessage(null, 0, MessagePipeHandle.ReadFlags.NONE); + if (result.getMojoResult() != MojoResult.RESOURCE_EXHAUSTED) { + return new ResultAnd<Boolean>(result.getMojoResult(), false); + } + ReadMessageResult readResult = result.getValue(); + assert readResult != null; + ByteBuffer buffer = ByteBuffer.allocateDirect(readResult.getMessageSize()); + result = handle.readMessage( + buffer, readResult.getHandlesCount(), MessagePipeHandle.ReadFlags.NONE); + if (receiver != null && result.getMojoResult() == MojoResult.OK) { + boolean accepted = receiver.accept(new Message(buffer, result.getValue().getHandles())); + return new ResultAnd<Boolean>(result.getMojoResult(), accepted); + } + return new ResultAnd<Boolean>(result.getMojoResult(), false); + } +} diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DataHeader.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DataHeader.java new file mode 100644 index 0000000..96acec9 --- /dev/null +++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DataHeader.java @@ -0,0 +1,70 @@ +// Copyright 2015 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. + +package org.chromium.mojo.bindings; + +/** + * The header for a mojo complex element. + */ +public final class DataHeader { + /** + * The size of a serialized header, in bytes. + */ + public static final int HEADER_SIZE = 8; + + /** + * The offset of the size field. + */ + public static final int SIZE_OFFSET = 0; + + /** + * The offset of the number of fields field. + */ + public static final int ELEMENTS_OR_VERSION_OFFSET = 4; + + /** + * The size of the object owning this header. + */ + public final int size; + + /** + * Number of element (for an array) or version (for a struct) of the object owning this + * header. + */ + public final int elementsOrVersion; + + /** + * Constructor. + */ + public DataHeader(int size, int elementsOrVersion) { + super(); + this.size = size; + this.elementsOrVersion = elementsOrVersion; + } + + /** + * @see Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + elementsOrVersion; + result = prime * result + size; + return result; + } + + /** + * @see Object#equals(Object) + */ + @Override + public boolean equals(Object object) { + if (object == this) return true; + if (object == null) return false; + if (getClass() != object.getClass()) return false; + + DataHeader other = (DataHeader) object; + return (elementsOrVersion == other.elementsOrVersion && size == other.size); + } +}
\ No newline at end of file diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Decoder.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Decoder.java new file mode 100644 index 0000000..868eab6 --- /dev/null +++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Decoder.java @@ -0,0 +1,714 @@ +// 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. + +package org.chromium.mojo.bindings; + +import org.chromium.mojo.bindings.Interface.Proxy; +import org.chromium.mojo.system.DataPipe; +import org.chromium.mojo.system.Handle; +import org.chromium.mojo.system.InvalidHandle; +import org.chromium.mojo.system.MessagePipeHandle; +import org.chromium.mojo.system.SharedBufferHandle; +import org.chromium.mojo.system.UntypedHandle; + +import java.nio.ByteOrder; +import java.nio.charset.Charset; + +/** + * A Decoder is a helper class for deserializing a mojo struct. It enables deserialization of basic + * types from a {@link Message} object at a given offset into it's byte buffer. + */ +public class Decoder { + + /** + * Helper class to validate the decoded message. + */ + static final class Validator { + + /** + * Minimal value for the next handle to deserialize. + */ + private int mMinNextClaimedHandle = 0; + /** + * Minimal value of the start of the next memory to claim. + */ + private long mMinNextMemory = 0; + + /** + * The maximal memory accessible. + */ + private final long mMaxMemory; + + /** + * The number of handles in the message. + */ + private final long mNumberOfHandles; + + /** + * Constructor. + */ + Validator(long maxMemory, int numberOfHandles) { + mMaxMemory = maxMemory; + mNumberOfHandles = numberOfHandles; + } + + public void claimHandle(int handle) { + if (handle < mMinNextClaimedHandle) { + throw new DeserializationException( + "Trying to access handle out of order."); + } + if (handle >= mNumberOfHandles) { + throw new DeserializationException("Trying to access non present handle."); + } + mMinNextClaimedHandle = handle + 1; + } + + public void claimMemory(long start, long end) { + if (start % BindingsHelper.ALIGNMENT != 0) { + throw new DeserializationException("Incorrect starting alignment: " + start + "."); + } + if (start < mMinNextMemory) { + throw new DeserializationException("Trying to access memory out of order."); + } + if (end < start) { + throw new DeserializationException("Incorrect memory range."); + } + if (end > mMaxMemory) { + throw new DeserializationException("Trying to access out of range memory."); + } + mMinNextMemory = BindingsHelper.align(end); + } + } + + /** + * The message to deserialize from. + */ + private final Message mMessage; + + /** + * The base offset in the byte buffer. + */ + private final int mBaseOffset; + + /** + * Validator for the decoded message. + */ + private final Validator mValidator; + + /** + * Constructor. + * + * @param message The message to decode. + */ + public Decoder(Message message) { + this(message, new Validator(message.getData().limit(), message.getHandles().size()), 0); + } + + private Decoder(Message message, Validator validator, int baseOffset) { + mMessage = message; + mMessage.getData().order(ByteOrder.LITTLE_ENDIAN); + mBaseOffset = baseOffset; + mValidator = validator; + } + + /** + * Deserializes a {@link DataHeader} at the current position. + */ + public DataHeader readDataHeader() { + // Claim the memory for the header. + mValidator.claimMemory(mBaseOffset, mBaseOffset + DataHeader.HEADER_SIZE); + DataHeader result = readDataHeaderAtOffset(0, false); + // Claim the rest of the memory. + mValidator.claimMemory(mBaseOffset + DataHeader.HEADER_SIZE, mBaseOffset + result.size); + return result; + } + + /** + * Deserializes a {@link DataHeader} for an union at the given offset. + */ + public DataHeader readDataHeaderForUnion(int offset) { + DataHeader result = readDataHeaderAtOffset(offset, true); + if (result.size == 0) { + if (result.elementsOrVersion != 0) { + throw new DeserializationException( + "Unexpected version tag for a null union. Expecting 0, found: " + + result.elementsOrVersion); + } + } else if (result.size != BindingsHelper.UNION_SIZE) { + throw new DeserializationException( + "Unexpected size of an union. The size must be 0 for a null union, or 16 for " + + "a non-null union."); + } + return result; + } + + /** + * @returns a decoder suitable to decode an union defined as the root object of a message. + */ + public Decoder decoderForSerializedUnion() { + mValidator.claimMemory(0, BindingsHelper.UNION_SIZE); + return this; + } + + /** + * Deserializes a {@link DataHeader} at the given offset. + */ + private DataHeader readDataHeaderAtOffset(int offset, boolean isUnion) { + int size = readInt(offset + DataHeader.SIZE_OFFSET); + int elementsOrVersion = readInt(offset + DataHeader.ELEMENTS_OR_VERSION_OFFSET); + if (size < 0) { + throw new DeserializationException( + "Negative size. Unsigned integers are not valid for java."); + } + if (elementsOrVersion < 0 && (!isUnion || elementsOrVersion != -1)) { + throw new DeserializationException( + "Negative elements or version. Unsigned integers are not valid for java."); + } + + return new DataHeader(size, elementsOrVersion); + } + + public DataHeader readAndValidateDataHeader(DataHeader[] versionArray) { + DataHeader header = readDataHeader(); + int maxVersionIndex = versionArray.length - 1; + if (header.elementsOrVersion <= versionArray[maxVersionIndex].elementsOrVersion) { + DataHeader referenceHeader = null; + for (int index = maxVersionIndex; index >= 0; index--) { + DataHeader dataHeader = versionArray[index]; + if (header.elementsOrVersion >= dataHeader.elementsOrVersion) { + referenceHeader = dataHeader; + break; + } + } + if (referenceHeader == null || referenceHeader.size != header.size) { + throw new DeserializationException( + "Header doesn't correspond to any known version."); + } + } else { + if (header.size < versionArray[maxVersionIndex].size) { + throw new DeserializationException("Message newer than the last known version" + + " cannot be shorter than required by the last known version."); + } + } + return header; + } + + /** + * Deserializes a {@link DataHeader} at the given offset and checks if it is correct for an + * array where elements are pointers. + */ + public DataHeader readDataHeaderForPointerArray(int expectedLength) { + return readDataHeaderForArray(BindingsHelper.POINTER_SIZE, expectedLength); + } + + /** + * Deserializes a {@link DataHeader} at the given offset and checks if it is correct for an + * array where elements are unions. + */ + public DataHeader readDataHeaderForUnionArray(int expectedLength) { + return readDataHeaderForArray(BindingsHelper.UNION_SIZE, expectedLength); + } + + /** + * Deserializes a {@link DataHeader} at the given offset and checks if it is correct for a map. + */ + public void readDataHeaderForMap() { + DataHeader si = readDataHeader(); + if (si.size != BindingsHelper.MAP_STRUCT_HEADER.size) { + throw new DeserializationException( + "Incorrect header for map. The size is incorrect."); + } + if (si.elementsOrVersion != BindingsHelper.MAP_STRUCT_HEADER.elementsOrVersion) { + throw new DeserializationException( + "Incorrect header for map. The version is incorrect."); + } + } + + /** + * Deserializes a byte at the given offset. + */ + public byte readByte(int offset) { + validateBufferSize(offset, 1); + return mMessage.getData().get(mBaseOffset + offset); + } + + /** + * Deserializes a boolean at the given offset, re-using any partially read byte. + */ + public boolean readBoolean(int offset, int bit) { + validateBufferSize(offset, 1); + return (readByte(offset) & (1 << bit)) != 0; + } + + /** + * Deserializes a short at the given offset. + */ + public short readShort(int offset) { + validateBufferSize(offset, 2); + return mMessage.getData().getShort(mBaseOffset + offset); + } + + /** + * Deserializes an int at the given offset. + */ + public int readInt(int offset) { + validateBufferSize(offset, 4); + return mMessage.getData().getInt(mBaseOffset + offset); + } + + /** + * Deserializes a float at the given offset. + */ + public float readFloat(int offset) { + validateBufferSize(offset, 4); + return mMessage.getData().getFloat(mBaseOffset + offset); + } + + /** + * Deserializes a long at the given offset. + */ + public long readLong(int offset) { + validateBufferSize(offset, 8); + return mMessage.getData().getLong(mBaseOffset + offset); + } + + /** + * Deserializes a double at the given offset. + */ + public double readDouble(int offset) { + validateBufferSize(offset, 8); + return mMessage.getData().getDouble(mBaseOffset + offset); + } + + /** + * Deserializes a pointer at the given offset. Returns a Decoder suitable to decode the content + * of the pointer. + */ + public Decoder readPointer(int offset, boolean nullable) { + int basePosition = mBaseOffset + offset; + long pointerOffset = readLong(offset); + if (pointerOffset == 0) { + if (!nullable) { + throw new DeserializationException( + "Trying to decode null pointer for a non-nullable type."); + } + return null; + } + int newPosition = (int) (basePosition + pointerOffset); + // The method |getDecoderAtPosition| will validate that the pointer address is valid. + return getDecoderAtPosition(newPosition); + + } + + /** + * Deserializes an array of boolean at the given offset. + */ + public boolean[] readBooleans(int offset, int arrayNullability, int expectedLength) { + Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); + if (d == null) { + return null; + } + DataHeader si = d.readDataHeaderForBooleanArray(expectedLength); + byte[] bytes = new byte[(si.elementsOrVersion + 7) / BindingsHelper.ALIGNMENT]; + d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE); + d.mMessage.getData().get(bytes); + boolean[] result = new boolean[si.elementsOrVersion]; + for (int i = 0; i < bytes.length; ++i) { + for (int j = 0; j < BindingsHelper.ALIGNMENT; ++j) { + int booleanIndex = i * BindingsHelper.ALIGNMENT + j; + if (booleanIndex < result.length) { + result[booleanIndex] = (bytes[i] & (1 << j)) != 0; + } + } + } + return result; + } + + /** + * Deserializes an array of bytes at the given offset. + */ + public byte[] readBytes(int offset, int arrayNullability, int expectedLength) { + Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); + if (d == null) { + return null; + } + DataHeader si = d.readDataHeaderForArray(1, expectedLength); + byte[] result = new byte[si.elementsOrVersion]; + d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE); + d.mMessage.getData().get(result); + return result; + } + + /** + * Deserializes an array of shorts at the given offset. + */ + public short[] readShorts(int offset, int arrayNullability, int expectedLength) { + Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); + if (d == null) { + return null; + } + DataHeader si = d.readDataHeaderForArray(2, expectedLength); + short[] result = new short[si.elementsOrVersion]; + d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE); + d.mMessage.getData().asShortBuffer().get(result); + return result; + } + + /** + * Deserializes an array of ints at the given offset. + */ + public int[] readInts(int offset, int arrayNullability, int expectedLength) { + Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); + if (d == null) { + return null; + } + DataHeader si = d.readDataHeaderForArray(4, expectedLength); + int[] result = new int[si.elementsOrVersion]; + d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE); + d.mMessage.getData().asIntBuffer().get(result); + return result; + } + + /** + * Deserializes an array of floats at the given offset. + */ + public float[] readFloats(int offset, int arrayNullability, int expectedLength) { + Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); + if (d == null) { + return null; + } + DataHeader si = d.readDataHeaderForArray(4, expectedLength); + float[] result = new float[si.elementsOrVersion]; + d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE); + d.mMessage.getData().asFloatBuffer().get(result); + return result; + } + + /** + * Deserializes an array of longs at the given offset. + */ + public long[] readLongs(int offset, int arrayNullability, int expectedLength) { + Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); + if (d == null) { + return null; + } + DataHeader si = d.readDataHeaderForArray(8, expectedLength); + long[] result = new long[si.elementsOrVersion]; + d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE); + d.mMessage.getData().asLongBuffer().get(result); + return result; + } + + /** + * Deserializes an array of doubles at the given offset. + */ + public double[] readDoubles(int offset, int arrayNullability, int expectedLength) { + Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); + if (d == null) { + return null; + } + DataHeader si = d.readDataHeaderForArray(8, expectedLength); + double[] result = new double[si.elementsOrVersion]; + d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE); + d.mMessage.getData().asDoubleBuffer().get(result); + return result; + } + + /** + * Deserializes an |Handle| at the given offset. + */ + public Handle readHandle(int offset, boolean nullable) { + int index = readInt(offset); + if (index == -1) { + if (!nullable) { + throw new DeserializationException( + "Trying to decode an invalid handle for a non-nullable type."); + } + return InvalidHandle.INSTANCE; + } + mValidator.claimHandle(index); + return mMessage.getHandles().get(index); + } + + /** + * Deserializes an |UntypedHandle| at the given offset. + */ + public UntypedHandle readUntypedHandle(int offset, boolean nullable) { + return readHandle(offset, nullable).toUntypedHandle(); + } + + /** + * Deserializes a |ConsumerHandle| at the given offset. + */ + public DataPipe.ConsumerHandle readConsumerHandle(int offset, boolean nullable) { + return readUntypedHandle(offset, nullable).toDataPipeConsumerHandle(); + } + + /** + * Deserializes a |ProducerHandle| at the given offset. + */ + public DataPipe.ProducerHandle readProducerHandle(int offset, boolean nullable) { + return readUntypedHandle(offset, nullable).toDataPipeProducerHandle(); + } + + /** + * Deserializes a |MessagePipeHandle| at the given offset. + */ + public MessagePipeHandle readMessagePipeHandle(int offset, boolean nullable) { + return readUntypedHandle(offset, nullable).toMessagePipeHandle(); + } + + /** + * Deserializes a |SharedBufferHandle| at the given offset. + */ + public SharedBufferHandle readSharedBufferHandle(int offset, boolean nullable) { + return readUntypedHandle(offset, nullable).toSharedBufferHandle(); + } + + /** + * Deserializes an interface at the given offset. + * + * @return a proxy to the service. + */ + public <P extends Proxy> P readServiceInterface(int offset, boolean nullable, + Interface.Manager<?, P> manager) { + MessagePipeHandle handle = readMessagePipeHandle(offset, nullable); + if (!handle.isValid()) { + return null; + } + int version = readInt(offset + BindingsHelper.SERIALIZED_HANDLE_SIZE); + return manager.attachProxy(handle, version); + } + + /** + * Deserializes a |InterfaceRequest| at the given offset. + */ + public <I extends Interface> InterfaceRequest<I> readInterfaceRequest(int offset, + boolean nullable) { + MessagePipeHandle handle = readMessagePipeHandle(offset, nullable); + if (handle == null) { + return null; + } + return new InterfaceRequest<I>(handle); + } + + /** + * Deserializes a string at the given offset. + */ + public String readString(int offset, boolean nullable) { + final int arrayNullability = nullable ? BindingsHelper.ARRAY_NULLABLE : 0; + byte[] bytes = readBytes(offset, arrayNullability, BindingsHelper.UNSPECIFIED_ARRAY_LENGTH); + if (bytes == null) { + return null; + } + return new String(bytes, Charset.forName("utf8")); + } + + /** + * Deserializes an array of |Handle| at the given offset. + */ + public Handle[] readHandles(int offset, int arrayNullability, int expectedLength) { + Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); + if (d == null) { + return null; + } + DataHeader si = d.readDataHeaderForArray(4, expectedLength); + Handle[] result = new Handle[si.elementsOrVersion]; + for (int i = 0; i < result.length; ++i) { + result[i] = d.readHandle( + DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i, + BindingsHelper.isElementNullable(arrayNullability)); + } + return result; + } + + /** + * Deserializes an array of |UntypedHandle| at the given offset. + */ + public UntypedHandle[] readUntypedHandles( + int offset, int arrayNullability, int expectedLength) { + Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); + if (d == null) { + return null; + } + DataHeader si = d.readDataHeaderForArray(4, expectedLength); + UntypedHandle[] result = new UntypedHandle[si.elementsOrVersion]; + for (int i = 0; i < result.length; ++i) { + result[i] = d.readUntypedHandle( + DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i, + BindingsHelper.isElementNullable(arrayNullability)); + } + return result; + } + + /** + * Deserializes an array of |ConsumerHandle| at the given offset. + */ + public DataPipe.ConsumerHandle[] readConsumerHandles( + int offset, int arrayNullability, int expectedLength) { + Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); + if (d == null) { + return null; + } + DataHeader si = d.readDataHeaderForArray(4, expectedLength); + DataPipe.ConsumerHandle[] result = new DataPipe.ConsumerHandle[si.elementsOrVersion]; + for (int i = 0; i < result.length; ++i) { + result[i] = d.readConsumerHandle( + DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i, + BindingsHelper.isElementNullable(arrayNullability)); + } + return result; + } + + /** + * Deserializes an array of |ProducerHandle| at the given offset. + */ + public DataPipe.ProducerHandle[] readProducerHandles( + int offset, int arrayNullability, int expectedLength) { + Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); + if (d == null) { + return null; + } + DataHeader si = d.readDataHeaderForArray(4, expectedLength); + DataPipe.ProducerHandle[] result = new DataPipe.ProducerHandle[si.elementsOrVersion]; + for (int i = 0; i < result.length; ++i) { + result[i] = d.readProducerHandle( + DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i, + BindingsHelper.isElementNullable(arrayNullability)); + } + return result; + + } + + /** + * Deserializes an array of |MessagePipeHandle| at the given offset. + */ + public MessagePipeHandle[] readMessagePipeHandles( + int offset, int arrayNullability, int expectedLength) { + Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); + if (d == null) { + return null; + } + DataHeader si = d.readDataHeaderForArray(4, expectedLength); + MessagePipeHandle[] result = new MessagePipeHandle[si.elementsOrVersion]; + for (int i = 0; i < result.length; ++i) { + result[i] = d.readMessagePipeHandle( + DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i, + BindingsHelper.isElementNullable(arrayNullability)); + } + return result; + + } + + /** + * Deserializes an array of |SharedBufferHandle| at the given offset. + */ + public SharedBufferHandle[] readSharedBufferHandles( + int offset, int arrayNullability, int expectedLength) { + Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); + if (d == null) { + return null; + } + DataHeader si = d.readDataHeaderForArray(4, expectedLength); + SharedBufferHandle[] result = new SharedBufferHandle[si.elementsOrVersion]; + for (int i = 0; i < result.length; ++i) { + result[i] = d.readSharedBufferHandle( + DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i, + BindingsHelper.isElementNullable(arrayNullability)); + } + return result; + + } + + /** + * Deserializes an array of |ServiceHandle| at the given offset. + */ + public <S extends Interface, P extends Proxy> S[] readServiceInterfaces( + int offset, int arrayNullability, int expectedLength, Interface.Manager<S, P> manager) { + Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); + if (d == null) { + return null; + } + DataHeader si = + d.readDataHeaderForArray(BindingsHelper.SERIALIZED_INTERFACE_SIZE, expectedLength); + S[] result = manager.buildArray(si.elementsOrVersion); + for (int i = 0; i < result.length; ++i) { + // This cast is necessary because java 6 doesn't handle wildcard correctly when using + // Manager<S, ? extends S> + @SuppressWarnings("unchecked") + S value = (S) d.readServiceInterface( + DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_INTERFACE_SIZE * i, + BindingsHelper.isElementNullable(arrayNullability), manager); + result[i] = value; + } + return result; + } + + /** + * Deserializes an array of |InterfaceRequest| at the given offset. + */ + public <I extends Interface> InterfaceRequest<I>[] readInterfaceRequests( + int offset, int arrayNullability, int expectedLength) { + Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); + if (d == null) { + return null; + } + DataHeader si = d.readDataHeaderForArray(4, expectedLength); + @SuppressWarnings("unchecked") + InterfaceRequest<I>[] result = new InterfaceRequest[si.elementsOrVersion]; + for (int i = 0; i < result.length; ++i) { + result[i] = d.readInterfaceRequest( + DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i, + BindingsHelper.isElementNullable(arrayNullability)); + } + return result; + } + + /** + * Returns a view of this decoder at the offset |offset|. + */ + private Decoder getDecoderAtPosition(int offset) { + return new Decoder(mMessage, mValidator, offset); + } + + /** + * Deserializes a {@link DataHeader} at the given offset and checks if it is correct for an + * array of booleans. + */ + private DataHeader readDataHeaderForBooleanArray(int expectedLength) { + DataHeader dataHeader = readDataHeader(); + if (dataHeader.size < DataHeader.HEADER_SIZE + (dataHeader.elementsOrVersion + 7) / 8) { + throw new DeserializationException("Array header is incorrect."); + } + if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH + && dataHeader.elementsOrVersion != expectedLength) { + throw new DeserializationException("Incorrect array length. Expected: " + expectedLength + + ", but got: " + dataHeader.elementsOrVersion + "."); + } + return dataHeader; + } + + /** + * Deserializes a {@link DataHeader} of an array at the given offset. + */ + private DataHeader readDataHeaderForArray(long elementSize, int expectedLength) { + DataHeader dataHeader = readDataHeader(); + if (dataHeader.size + < (DataHeader.HEADER_SIZE + elementSize * dataHeader.elementsOrVersion)) { + throw new DeserializationException("Array header is incorrect."); + } + if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH + && dataHeader.elementsOrVersion != expectedLength) { + throw new DeserializationException("Incorrect array length. Expected: " + expectedLength + + ", but got: " + dataHeader.elementsOrVersion + "."); + } + return dataHeader; + } + + private void validateBufferSize(int offset, int size) { + if (mMessage.getData().limit() < offset + size) { + throw new DeserializationException("Buffer is smaller than expected."); + } + } +} diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DelegatingConnectionErrorHandler.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DelegatingConnectionErrorHandler.java new file mode 100644 index 0000000..2ee2ae7 --- /dev/null +++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DelegatingConnectionErrorHandler.java @@ -0,0 +1,49 @@ +// 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. + +package org.chromium.mojo.bindings; + +import org.chromium.mojo.system.MojoException; + +import java.util.Collections; +import java.util.Set; +import java.util.WeakHashMap; + +/** + * A {@link ConnectionErrorHandler} that delegate the errors to a list of registered handlers. This + * class will use weak pointers to prevent keeping references to any handlers it delegates to. + */ +public class DelegatingConnectionErrorHandler implements ConnectionErrorHandler { + + /** + * The registered handlers. This uses a {@link WeakHashMap} so that it doesn't prevent the + * handler from being garbage collected. + */ + private final Set<ConnectionErrorHandler> mHandlers = + Collections.newSetFromMap(new WeakHashMap<ConnectionErrorHandler, Boolean>()); + + /** + * @see ConnectionErrorHandler#onConnectionError(MojoException) + */ + @Override + public void onConnectionError(MojoException e) { + for (ConnectionErrorHandler handler : mHandlers) { + handler.onConnectionError(e); + } + } + + /** + * Add a handler that will be notified of any error this object receives. + */ + public void addConnectionErrorHandler(ConnectionErrorHandler handler) { + mHandlers.add(handler); + } + + /** + * Remove a previously registered handler. + */ + public void removeConnectionErrorHandler(ConnectionErrorHandler handler) { + mHandlers.remove(handler); + } +} diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DeserializationException.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DeserializationException.java new file mode 100644 index 0000000..eeb511c --- /dev/null +++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DeserializationException.java @@ -0,0 +1,26 @@ +// 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. + +package org.chromium.mojo.bindings; + +/** + * Error when deserializing a mojo message. + */ +public class DeserializationException extends RuntimeException { + + /** + * Constructs a new deserialization exception with the specified detail message. + */ + public DeserializationException(String message) { + super(message); + } + + /** + * Constructs a new deserialization exception with the specified cause. + */ + public DeserializationException(Exception cause) { + super(cause); + } + +} diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Encoder.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Encoder.java new file mode 100644 index 0000000..3541f21 --- /dev/null +++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Encoder.java @@ -0,0 +1,563 @@ +// 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. + +package org.chromium.mojo.bindings; + +import org.chromium.mojo.bindings.Interface.Proxy.Handler; +import org.chromium.mojo.system.Core; +import org.chromium.mojo.system.Handle; +import org.chromium.mojo.system.MessagePipeHandle; +import org.chromium.mojo.system.Pair; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; + +/** + * Helper class to encode a mojo struct. It keeps track of the output buffer, resizing it as needed. + * It also keeps track of the associated handles, and the offset of the current data section. + */ +public class Encoder { + + /** + * Container class for all state that must be shared between the main encoder and any used sub + * encoder. + */ + private static class EncoderState { + + /** + * The core used to encode interfaces. + */ + public final Core core; + + /** + * The ByteBuffer to which the message will be encoded. + */ + public ByteBuffer byteBuffer; + + /** + * The list of encountered handles. + */ + public final List<Handle> handles = new ArrayList<Handle>(); + + /** + * The current absolute position for the next data section. + */ + public int dataEnd; + + /** + * @param core the |Core| implementation used to generate handles. Only used if the data + * structure being encoded contains interfaces, can be |null| otherwise. + * @param bufferSize A hint on the size of the message. Used to build the initial byte + * buffer. + */ + private EncoderState(Core core, int bufferSize) { + assert bufferSize % BindingsHelper.ALIGNMENT == 0; + this.core = core; + byteBuffer = ByteBuffer.allocateDirect( + bufferSize > 0 ? bufferSize : INITIAL_BUFFER_SIZE); + byteBuffer.order(ByteOrder.LITTLE_ENDIAN); + dataEnd = 0; + } + + /** + * Claim the given amount of memory at the end of the buffer, resizing it if needed. + */ + public void claimMemory(int size) { + dataEnd += size; + growIfNeeded(); + } + + /** + * Grow the associated ByteBuffer if needed. + */ + private void growIfNeeded() { + if (byteBuffer.capacity() >= dataEnd) { + return; + } + int targetSize = byteBuffer.capacity() * 2; + while (targetSize < dataEnd) { + targetSize *= 2; + } + ByteBuffer newBuffer = ByteBuffer.allocateDirect(targetSize); + newBuffer.order(ByteOrder.nativeOrder()); + byteBuffer.position(0); + byteBuffer.limit(byteBuffer.capacity()); + newBuffer.put(byteBuffer); + byteBuffer = newBuffer; + } + } + + /** + * Default initial size of the data buffer. This must be a multiple of 8 bytes. + */ + private static final int INITIAL_BUFFER_SIZE = 1024; + + /** + * Base offset in the byte buffer for writing. + */ + private int mBaseOffset; + + /** + * The encoder state shared by the main encoder and all its sub-encoder. + */ + private final EncoderState mEncoderState; + + /** + * Returns the result message. + */ + public Message getMessage() { + mEncoderState.byteBuffer.position(0); + mEncoderState.byteBuffer.limit(mEncoderState.dataEnd); + return new Message(mEncoderState.byteBuffer, mEncoderState.handles); + } + + /** + * Constructor. + * + * @param core the |Core| implementation used to generate handles. Only used if the data + * structure being encoded contains interfaces, can be |null| otherwise. + * @param sizeHint A hint on the size of the message. Used to build the initial byte buffer. + */ + public Encoder(Core core, int sizeHint) { + this(new EncoderState(core, sizeHint)); + } + + /** + * Private constructor for sub-encoders. + */ + private Encoder(EncoderState bufferInformation) { + mEncoderState = bufferInformation; + mBaseOffset = bufferInformation.dataEnd; + } + + /** + * Returns a new encoder that will append to the current buffer. + */ + public Encoder getEncoderAtDataOffset(DataHeader dataHeader) { + Encoder result = new Encoder(mEncoderState); + result.encode(dataHeader); + return result; + } + + /** + * Encode a {@link DataHeader} and claim the amount of memory required for the data section + * (resizing the buffer if required). + */ + public void encode(DataHeader s) { + mEncoderState.claimMemory(BindingsHelper.align(s.size)); + encode(s.size, DataHeader.SIZE_OFFSET); + encode(s.elementsOrVersion, DataHeader.ELEMENTS_OR_VERSION_OFFSET); + } + + /** + * Encode a byte at the given offset. + */ + public void encode(byte v, int offset) { + mEncoderState.byteBuffer.put(mBaseOffset + offset, v); + } + + /** + * Encode a boolean at the given offset. + */ + public void encode(boolean v, int offset, int bit) { + if (v) { + byte encodedValue = mEncoderState.byteBuffer.get(mBaseOffset + offset); + encodedValue |= (byte) (1 << bit); + mEncoderState.byteBuffer.put(mBaseOffset + offset, encodedValue); + } + } + + /** + * Encode a short at the given offset. + */ + public void encode(short v, int offset) { + mEncoderState.byteBuffer.putShort(mBaseOffset + offset, v); + } + + /** + * Encode an int at the given offset. + */ + public void encode(int v, int offset) { + mEncoderState.byteBuffer.putInt(mBaseOffset + offset, v); + } + + /** + * Encode a float at the given offset. + */ + public void encode(float v, int offset) { + mEncoderState.byteBuffer.putFloat(mBaseOffset + offset, v); + } + + /** + * Encode a long at the given offset. + */ + public void encode(long v, int offset) { + mEncoderState.byteBuffer.putLong(mBaseOffset + offset, v); + } + + /** + * Encode a double at the given offset. + */ + public void encode(double v, int offset) { + mEncoderState.byteBuffer.putDouble(mBaseOffset + offset, v); + } + + /** + * Encode a {@link Struct} at the given offset. + */ + public void encode(Struct v, int offset, boolean nullable) { + if (v == null) { + encodeNullPointer(offset, nullable); + return; + } + encodePointerToNextUnclaimedData(offset); + v.encode(this); + } + + /** + * Encode a {@link Union} at the given offset. + */ + public void encode(Union v, int offset, boolean nullable) { + if (v == null && !nullable) { + throw new SerializationException( + "Trying to encode a null pointer for a non-nullable type."); + } + if (v == null) { + encode(0L, offset); + encode(0L, offset + DataHeader.HEADER_SIZE); + return; + } + v.encode(this, offset); + } + + /** + * Encodes a String. + */ + public void encode(String v, int offset, boolean nullable) { + if (v == null) { + encodeNullPointer(offset, nullable); + return; + } + final int arrayNullability = nullable + ? BindingsHelper.ARRAY_NULLABLE : BindingsHelper.NOTHING_NULLABLE; + encode(v.getBytes(Charset.forName("utf8")), offset, arrayNullability, + BindingsHelper.UNSPECIFIED_ARRAY_LENGTH); + } + + /** + * Encodes a {@link Handle}. + */ + public void encode(Handle v, int offset, boolean nullable) { + if (v == null || !v.isValid()) { + encodeInvalidHandle(offset, nullable); + } else { + encode(mEncoderState.handles.size(), offset); + mEncoderState.handles.add(v); + } + } + + /** + * Encode an {@link Interface}. + */ + public <T extends Interface> void encode(T v, int offset, boolean nullable, + Interface.Manager<T, ?> manager) { + if (v == null) { + encodeInvalidHandle(offset, nullable); + encode(0, offset + BindingsHelper.SERIALIZED_HANDLE_SIZE); + return; + } + if (mEncoderState.core == null) { + throw new UnsupportedOperationException( + "The encoder has been created without a Core. It can't encode an interface."); + } + // If the instance is a proxy, pass the proxy's handle instead of creating a new stub. + if (v instanceof Interface.Proxy) { + Handler handler = ((Interface.Proxy) v).getProxyHandler(); + encode(handler.passHandle(), offset, nullable); + encode(handler.getVersion(), offset + BindingsHelper.SERIALIZED_HANDLE_SIZE); + return; + } + Pair<MessagePipeHandle, MessagePipeHandle> handles = + mEncoderState.core.createMessagePipe(null); + manager.bind(v, handles.first); + encode(handles.second, offset, nullable); + encode(manager.getVersion(), offset + BindingsHelper.SERIALIZED_HANDLE_SIZE); + } + + /** + * Encode an {@link InterfaceRequest}. + */ + public <I extends Interface> void encode(InterfaceRequest<I> v, int offset, boolean nullable) { + if (v == null) { + encodeInvalidHandle(offset, nullable); + return; + } + if (mEncoderState.core == null) { + throw new UnsupportedOperationException( + "The encoder has been created without a Core. It can't encode an interface."); + } + encode(v.passHandle(), offset, nullable); + } + + /** + * Returns an {@link Encoder} suitable for encoding an array of pointer of the given length. + */ + public Encoder encodePointerArray(int length, int offset, int expectedLength) { + return encoderForArray(BindingsHelper.POINTER_SIZE, length, offset, expectedLength); + } + + /** + * Returns an {@link Encoder} suitable for encoding an array of union of the given length. + */ + public Encoder encodeUnionArray(int length, int offset, int expectedLength) { + return encoderForArray(BindingsHelper.UNION_SIZE, length, offset, expectedLength); + } + + /** + * Encodes an array of booleans. + */ + public void encode(boolean[] v, int offset, int arrayNullability, int expectedLength) { + if (v == null) { + encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); + return; + } + if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH + && expectedLength != v.length) { + throw new SerializationException("Trying to encode a fixed array of incorrect length."); + } + byte[] bytes = new byte[(v.length + 7) / BindingsHelper.ALIGNMENT]; + for (int i = 0; i < bytes.length; ++i) { + for (int j = 0; j < BindingsHelper.ALIGNMENT; ++j) { + int booleanIndex = BindingsHelper.ALIGNMENT * i + j; + if (booleanIndex < v.length && v[booleanIndex]) { + bytes[i] |= (byte) (1 << j); + } + } + } + encodeByteArray(bytes, v.length, offset); + } + + /** + * Encodes an array of bytes. + */ + public void encode(byte[] v, int offset, int arrayNullability, int expectedLength) { + if (v == null) { + encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); + return; + } + if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH + && expectedLength != v.length) { + throw new SerializationException("Trying to encode a fixed array of incorrect length."); + } + encodeByteArray(v, v.length, offset); + } + + /** + * Encodes an array of shorts. + */ + public void encode(short[] v, int offset, int arrayNullability, int expectedLength) { + if (v == null) { + encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); + return; + } + encoderForArray(2, v.length, offset, expectedLength).append(v); + } + + /** + * Encodes an array of ints. + */ + public void encode(int[] v, int offset, int arrayNullability, int expectedLength) { + if (v == null) { + encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); + return; + } + encoderForArray(4, v.length, offset, expectedLength).append(v); + } + + /** + * Encodes an array of floats. + */ + public void encode(float[] v, int offset, int arrayNullability, int expectedLength) { + if (v == null) { + encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); + return; + } + encoderForArray(4, v.length, offset, expectedLength).append(v); + } + + /** + * Encodes an array of longs. + */ + public void encode(long[] v, int offset, int arrayNullability, int expectedLength) { + if (v == null) { + encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); + return; + } + encoderForArray(8, v.length, offset, expectedLength).append(v); + } + + /** + * Encodes an array of doubles. + */ + public void encode(double[] v, int offset, int arrayNullability, int expectedLength) { + if (v == null) { + encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); + return; + } + encoderForArray(8, v.length, offset, expectedLength).append(v); + } + + /** + * Encodes an array of {@link Handle}. + */ + public void encode(Handle[] v, int offset, int arrayNullability, int expectedLength) { + if (v == null) { + encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); + return; + } + Encoder e = encoderForArray( + BindingsHelper.SERIALIZED_HANDLE_SIZE, v.length, offset, expectedLength); + for (int i = 0; i < v.length; ++i) { + e.encode(v[i], DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i, + BindingsHelper.isElementNullable(arrayNullability)); + } + } + + /** + * Encodes an array of {@link Interface}. + */ + public <T extends Interface> void encode(T[] v, int offset, int arrayNullability, + int expectedLength, Interface.Manager<T, ?> manager) { + if (v == null) { + encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); + return; + } + Encoder e = encoderForArray( + BindingsHelper.SERIALIZED_INTERFACE_SIZE, v.length, offset, expectedLength); + for (int i = 0; i < v.length; ++i) { + e.encode(v[i], DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_INTERFACE_SIZE * i, + BindingsHelper.isElementNullable(arrayNullability), manager); + } + } + + public Encoder encoderForMap(int offset) { + encodePointerToNextUnclaimedData(offset); + return getEncoderAtDataOffset(BindingsHelper.MAP_STRUCT_HEADER); + } + + /** + * Encodes a pointer to the next unclaimed memory and returns an encoder suitable to encode an + * union at this location. + */ + public Encoder encoderForUnionPointer(int offset) { + encodePointerToNextUnclaimedData(offset); + Encoder result = new Encoder(mEncoderState); + result.mEncoderState.claimMemory(16); + return result; + } + + /** + * Encodes an array of {@link InterfaceRequest}. + */ + public <I extends Interface> void encode(InterfaceRequest<I>[] v, int offset, + int arrayNullability, int expectedLength) { + if (v == null) { + encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); + return; + } + Encoder e = encoderForArray( + BindingsHelper.SERIALIZED_HANDLE_SIZE, v.length, offset, expectedLength); + for (int i = 0; i < v.length; ++i) { + e.encode(v[i], DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i, + BindingsHelper.isElementNullable(arrayNullability)); + } + } + + /** + * Encodes a <code>null</code> pointer iff the object is nullable, raises an exception + * otherwise. + */ + public void encodeNullPointer(int offset, boolean nullable) { + if (!nullable) { + throw new SerializationException( + "Trying to encode a null pointer for a non-nullable type."); + } + mEncoderState.byteBuffer.putLong(mBaseOffset + offset, 0); + } + + /** + * Encodes an invalid handle iff the object is nullable, raises an exception otherwise. + */ + public void encodeInvalidHandle(int offset, boolean nullable) { + if (!nullable) { + throw new SerializationException( + "Trying to encode an invalid handle for a non-nullable type."); + } + mEncoderState.byteBuffer.putInt(mBaseOffset + offset, -1); + } + + /** + * Claim the given amount of memory at the end of the buffer, resizing it if needed. + */ + void claimMemory(int size) { + mEncoderState.claimMemory(BindingsHelper.align(size)); + } + + private void encodePointerToNextUnclaimedData(int offset) { + encode((long) mEncoderState.dataEnd - (mBaseOffset + offset), offset); + } + + private Encoder encoderForArray( + int elementSizeInByte, int length, int offset, int expectedLength) { + if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH + && expectedLength != length) { + throw new SerializationException("Trying to encode a fixed array of incorrect length."); + } + return encoderForArrayByTotalSize(length * elementSizeInByte, length, offset); + } + + private Encoder encoderForArrayByTotalSize(int byteSize, int length, int offset) { + encodePointerToNextUnclaimedData(offset); + return getEncoderAtDataOffset( + new DataHeader(DataHeader.HEADER_SIZE + byteSize, length)); + } + + private void encodeByteArray(byte[] bytes, int length, int offset) { + encoderForArrayByTotalSize(bytes.length, length, offset).append(bytes); + } + + private void append(byte[] v) { + mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE); + mEncoderState.byteBuffer.put(v); + } + + private void append(short[] v) { + mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE); + mEncoderState.byteBuffer.asShortBuffer().put(v); + } + + private void append(int[] v) { + mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE); + mEncoderState.byteBuffer.asIntBuffer().put(v); + } + + private void append(float[] v) { + mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE); + mEncoderState.byteBuffer.asFloatBuffer().put(v); + } + + private void append(double[] v) { + mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE); + mEncoderState.byteBuffer.asDoubleBuffer().put(v); + } + + private void append(long[] v) { + mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE); + mEncoderState.byteBuffer.asLongBuffer().put(v); + } + +} diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ExecutorFactory.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ExecutorFactory.java new file mode 100644 index 0000000..c621d9b --- /dev/null +++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ExecutorFactory.java @@ -0,0 +1,185 @@ +// 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. + +package org.chromium.mojo.bindings; + +import org.chromium.mojo.system.AsyncWaiter; +import org.chromium.mojo.system.AsyncWaiter.Callback; +import org.chromium.mojo.system.Core; +import org.chromium.mojo.system.MessagePipeHandle; +import org.chromium.mojo.system.MessagePipeHandle.ReadMessageResult; +import org.chromium.mojo.system.MojoException; +import org.chromium.mojo.system.MojoResult; +import org.chromium.mojo.system.Pair; +import org.chromium.mojo.system.ResultAnd; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Executor; + +/** + * A factory which provides per-thread executors, which enable execution on the thread from which + * they were obtained. + */ +class ExecutorFactory { + + /** + * A null buffer which is used to send messages without any data on the PipedExecutor's + * signaling handles. + */ + private static final ByteBuffer NOTIFY_BUFFER = null; + + /** + * Implementation of the executor which uses a pair of {@link MessagePipeHandle} for signaling. + * The executor will wait asynchronously on one end of a {@link MessagePipeHandle} on the thread + * on which it was created. Other threads can call execute with a {@link Runnable}, and the + * executor will queue the {@link Runnable} and write a message on the other end of the handle. + * This will wake up the executor which is waiting on the handle, which will then dequeue the + * {@link Runnable} and execute it on the original thread. + */ + private static class PipedExecutor implements Executor, Callback { + + /** + * The handle which is written to. Access to this object must be protected with |mLock|. + */ + private final MessagePipeHandle mWriteHandle; + /** + * The handle which is read from. + */ + private final MessagePipeHandle mReadHandle; + /** + * The list of actions left to be run. Access to this object must be protected with |mLock|. + */ + private final List<Runnable> mPendingActions; + /** + * Lock protecting access to |mWriteHandle| and |mPendingActions|. + */ + private final Object mLock; + /** + * The {@link AsyncWaiter} to get notified of new message availability on |mReadHandle|. + */ + private final AsyncWaiter mWaiter; + + /** + * Constructor. + */ + public PipedExecutor(Core core) { + mWaiter = core.getDefaultAsyncWaiter(); + assert mWaiter != null; + mLock = new Object(); + Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe( + new MessagePipeHandle.CreateOptions()); + mReadHandle = handles.first; + mWriteHandle = handles.second; + mPendingActions = new ArrayList<Runnable>(); + asyncWait(); + } + + /** + * Asynchronously wait for the next command to arrive. This should only be called on the + * executor thread. + */ + private void asyncWait() { + mWaiter.asyncWait(mReadHandle, Core.HandleSignals.READABLE, Core.DEADLINE_INFINITE, + this); + } + + /** + * @see Callback#onResult(int) + */ + @Override + public void onResult(int result) { + if (result == MojoResult.OK && readNotifyBufferMessage()) { + runNextAction(); + } else { + close(); + } + } + + /** + * @see Callback#onError(MojoException) + */ + @Override + public void onError(MojoException exception) { + close(); + } + + /** + * Close the handles. Should only be called on the executor thread. + */ + private void close() { + synchronized (mLock) { + mWriteHandle.close(); + mPendingActions.clear(); + } + mReadHandle.close(); + } + + /** + * Read the next message on |mReadHandle|, and return |true| if successful, |false| + * otherwise. + */ + private boolean readNotifyBufferMessage() { + try { + ResultAnd<ReadMessageResult> readMessageResult = + mReadHandle.readMessage(NOTIFY_BUFFER, 0, MessagePipeHandle.ReadFlags.NONE); + if (readMessageResult.getMojoResult() == MojoResult.OK) { + asyncWait(); + return true; + } + } catch (MojoException e) { + // Will be closed by the fall back at the end of this method. + } + return false; + } + + /** + * Run the next action in the |mPendingActions| queue. + */ + private void runNextAction() { + Runnable toRun = null; + synchronized (mLock) { + toRun = mPendingActions.remove(0); + } + toRun.run(); + } + + /** + * Execute the given |command| in the executor thread. This can be called on any thread. + * + * @see Executor#execute(Runnable) + */ + @Override + public void execute(Runnable command) { + // Accessing the write handle must be protected by the lock, because it can be closed + // from the executor's thread. + synchronized (mLock) { + if (!mWriteHandle.isValid()) { + throw new IllegalStateException( + "Trying to execute an action on a closed executor."); + } + mPendingActions.add(command); + mWriteHandle.writeMessage(NOTIFY_BUFFER, null, MessagePipeHandle.WriteFlags.NONE); + } + } + } + + /** + * Keep one executor per executor thread. + */ + private static final ThreadLocal<Executor> EXECUTORS = new ThreadLocal<Executor>(); + + /** + * Returns an {@link Executor} that will run all of its actions in the current thread. + */ + public static Executor getExecutorForCurrentThread(Core core) { + Executor executor = EXECUTORS.get(); + if (executor == null) { + executor = new PipedExecutor(core); + EXECUTORS.set(executor); + } + return executor; + } +} diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/HandleOwner.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/HandleOwner.java new file mode 100644 index 0000000..60fc33ba --- /dev/null +++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/HandleOwner.java @@ -0,0 +1,29 @@ +// 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. + +package org.chromium.mojo.bindings; + +import org.chromium.mojo.system.Handle; + +import java.io.Closeable; + +/** + * Describes a class that owns a handle. + * + * @param <H> The type of the owned handle. + */ +public interface HandleOwner<H extends Handle> extends Closeable { + + /** + * Pass the handle owned by this class. + */ + public H passHandle(); + + /** + * @see java.io.Closeable#close() + */ + @Override + public void close(); + +} diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Interface.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Interface.java new file mode 100644 index 0000000..c2bbc8e --- /dev/null +++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Interface.java @@ -0,0 +1,409 @@ +// 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. + +package org.chromium.mojo.bindings; + +import org.chromium.mojo.bindings.Callbacks.Callback1; +import org.chromium.mojo.bindings.Interface.AbstractProxy.HandlerImpl; +import org.chromium.mojo.system.Core; +import org.chromium.mojo.system.MessagePipeHandle; +import org.chromium.mojo.system.MojoException; +import org.chromium.mojo.system.Pair; + +import java.io.Closeable; + +/** + * Base class for mojo generated interfaces. + */ +public interface Interface extends ConnectionErrorHandler, Closeable { + + /** + * The close method is called when the connection to the interface is closed. + * + * @see java.io.Closeable#close() + */ + @Override + public void close(); + + /** + * A proxy to a mojo interface. This is base class for all generated proxies. It implements the + * Interface and each time a method is called, the parameters are serialized and sent to the + * {@link MessageReceiverWithResponder}, along with the response callback if needed. + */ + public interface Proxy extends Interface { + /** + * Class allowing to interact with the proxy itself. + */ + public interface Handler extends Closeable { + /** + * Sets the {@link ConnectionErrorHandler} that will be notified of errors. + */ + public void setErrorHandler(ConnectionErrorHandler errorHandler); + + /** + * Unbinds the proxy and passes the handle. Can return null if the proxy is not bound or + * if the proxy is not over a message pipe. + */ + public MessagePipeHandle passHandle(); + + /** + * Returns the version number of the interface that the remote side supports. + */ + public int getVersion(); + + /** + * Queries the max version that the remote side supports. On completion, the result will + * be returned as the input of |callback|. The version number of this interface pointer + * will also be updated. + */ + public void queryVersion(Callback1<Integer> callback); + + /** + * If the remote side doesn't support the specified version, it will close its end of + * the message pipe asynchronously. The call does nothing if |version| is no greater + * than getVersion(). + * <p> + * If you make a call to requireVersion() with a version number X which is not supported + * by the remote side, it is guaranteed that all calls to the interface methods after + * requireVersion(X) will be ignored. + */ + public void requireVersion(int version); + } + + /** + * Returns the {@link Handler} object allowing to interact with the proxy itself. + */ + public Handler getProxyHandler(); + } + + /** + * Base implementation of {@link Proxy}. + */ + abstract class AbstractProxy implements Proxy { + /** + * Implementation of {@link Handler}. + */ + protected static class HandlerImpl implements Proxy.Handler, ConnectionErrorHandler { + /** + * The {@link Core} implementation to use. + */ + private final Core mCore; + + /** + * The {@link MessageReceiverWithResponder} that will receive a serialized message for + * each method call. + */ + private final MessageReceiverWithResponder mMessageReceiver; + + /** + * The {@link ConnectionErrorHandler} that will be notified of errors. + */ + private ConnectionErrorHandler mErrorHandler = null; + + /** + * The currently known version of the interface. + */ + private int mVersion = 0; + + /** + * Constructor. + * + * @param core the Core implementation used to create pipes and access the async waiter. + * @param messageReceiver the message receiver to send message to. + */ + protected HandlerImpl(Core core, MessageReceiverWithResponder messageReceiver) { + this.mCore = core; + this.mMessageReceiver = messageReceiver; + } + + void setVersion(int version) { + mVersion = version; + } + + /** + * Returns the message receiver to send message to. + */ + public MessageReceiverWithResponder getMessageReceiver() { + return mMessageReceiver; + } + + /** + * Returns the Core implementation. + */ + public Core getCore() { + return mCore; + } + + /** + * Sets the {@link ConnectionErrorHandler} that will be notified of errors. + */ + @Override + public void setErrorHandler(ConnectionErrorHandler errorHandler) { + this.mErrorHandler = errorHandler; + } + + /** + * @see ConnectionErrorHandler#onConnectionError(MojoException) + */ + @Override + public void onConnectionError(MojoException e) { + if (mErrorHandler != null) { + mErrorHandler.onConnectionError(e); + } + } + + /** + * @see Closeable#close() + */ + @Override + public void close() { + mMessageReceiver.close(); + } + + /** + * @see Interface.Proxy.Handler#passHandle() + */ + @Override + public MessagePipeHandle passHandle() { + @SuppressWarnings("unchecked") + HandleOwner<MessagePipeHandle> handleOwner = + (HandleOwner<MessagePipeHandle>) mMessageReceiver; + return handleOwner.passHandle(); + } + + /** + * @see Handler#getVersion() + */ + @Override + public int getVersion() { + return mVersion; + } + + /** + * @see Handler#queryVersion(org.chromium.mojo.bindings.Callbacks.Callback1) + */ + @Override + public void queryVersion(final Callback1<Integer> callback) { + RunMessageParams message = new RunMessageParams(); + message.reserved0 = 16; + message.reserved1 = 0; + message.queryVersion = new QueryVersion(); + + InterfaceControlMessagesHelper.sendRunMessage(getCore(), mMessageReceiver, message, + new Callback1<RunResponseMessageParams>() { + @Override + public void call(RunResponseMessageParams response) { + mVersion = response.queryVersionResult.version; + callback.call(mVersion); + } + }); + } + + /** + * @see Handler#requireVersion(int) + */ + @Override + public void requireVersion(int version) { + if (mVersion >= version) { + return; + } + mVersion = version; + RunOrClosePipeMessageParams message = new RunOrClosePipeMessageParams(); + message.reserved0 = 16; + message.reserved1 = 0; + message.requireVersion = new RequireVersion(); + message.requireVersion.version = version; + InterfaceControlMessagesHelper.sendRunOrClosePipeMessage( + getCore(), mMessageReceiver, message); + } + } + + /** + * The handler associated with this proxy. + */ + private final HandlerImpl mHandler; + + protected AbstractProxy(Core core, MessageReceiverWithResponder messageReceiver) { + mHandler = new HandlerImpl(core, messageReceiver); + } + + /** + * @see Interface#close() + */ + @Override + public void close() { + mHandler.close(); + } + + /** + * @see Proxy#getProxyHandler() + */ + @Override + public HandlerImpl getProxyHandler() { + return mHandler; + } + + /** + * @see ConnectionErrorHandler#onConnectionError(org.chromium.mojo.system.MojoException) + */ + @Override + public void onConnectionError(MojoException e) { + mHandler.onConnectionError(e); + } + } + + /** + * Base implementation of Stub. Stubs are message receivers that deserialize the payload and + * call the appropriate method in the implementation. If the method returns result, the stub + * serializes the response and sends it back. + * + * @param <I> the type of the interface to delegate calls to. + */ + abstract class Stub<I extends Interface> implements MessageReceiverWithResponder { + + /** + * The {@link Core} implementation to use. + */ + private final Core mCore; + + /** + * The implementation to delegate calls to. + */ + private final I mImpl; + + /** + * Constructor. + * + * @param core the {@link Core} implementation to use. + * @param impl the implementation to delegate calls to. + */ + public Stub(Core core, I impl) { + mCore = core; + mImpl = impl; + } + + /** + * Returns the Core implementation. + */ + protected Core getCore() { + return mCore; + } + + /** + * Returns the implementation to delegate calls to. + */ + protected I getImpl() { + return mImpl; + } + + /** + * @see org.chromium.mojo.bindings.MessageReceiver#close() + */ + @Override + public void close() { + mImpl.close(); + } + + } + + /** + * The |Manager| object enables building of proxies and stubs for a given interface. + * + * @param <I> the type of the interface the manager can handle. + * @param <P> the type of the proxy the manager can handle. To be noted, P always extends I. + */ + abstract class Manager<I extends Interface, P extends Proxy> { + + /** + * Returns the name of the interface. This is an opaque (but human readable) identifier used + * by the service provider to identify services. + */ + public abstract String getName(); + + /** + * Returns the version of the managed interface. + */ + public abstract int getVersion(); + + /** + * Binds the given implementation to the handle. + */ + public void bind(I impl, MessagePipeHandle handle) { + // The router (and by consequence the handle) is intentionally leaked. It will close + // itself when the connected handle is closed and the proxy receives the connection + // error. + Router router = new RouterImpl(handle); + bind(handle.getCore(), impl, router); + router.start(); + } + + /** + * Binds the given implementation to the InterfaceRequest. + */ + public final void bind(I impl, InterfaceRequest<I> request) { + bind(impl, request.passHandle()); + } + + /** + * Returns a Proxy that will send messages to the given |handle|. This implies that the + * other end of the handle must be bound to an implementation of the interface. + */ + public final P attachProxy(MessagePipeHandle handle, int version) { + RouterImpl router = new RouterImpl(handle); + P proxy = attachProxy(handle.getCore(), router); + DelegatingConnectionErrorHandler handlers = new DelegatingConnectionErrorHandler(); + handlers.addConnectionErrorHandler(proxy); + router.setErrorHandler(handlers); + router.start(); + ((HandlerImpl) proxy.getProxyHandler()).setVersion(version); + return proxy; + } + + /** + * Constructs a new |InterfaceRequest| for the interface. This method returns a Pair where + * the first element is a proxy, and the second element is the request. The proxy can be + * used immediately. + */ + public final Pair<P, InterfaceRequest<I>> getInterfaceRequest(Core core) { + Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null); + P proxy = attachProxy(handles.first, 0); + return Pair.create(proxy, new InterfaceRequest<I>(handles.second)); + } + + public final InterfaceRequest<I> asInterfaceRequest(MessagePipeHandle handle) { + return new InterfaceRequest<I>(handle); + } + + /** + * Binds the implementation to the given |router|. + */ + final void bind(Core core, I impl, Router router) { + router.setErrorHandler(impl); + router.setIncomingMessageReceiver(buildStub(core, impl)); + } + + /** + * Returns a Proxy that will send messages to the given |router|. + */ + final P attachProxy(Core core, Router router) { + return buildProxy(core, new AutoCloseableRouter(core, router)); + } + + /** + * Creates a new array of the given |size|. + */ + protected abstract I[] buildArray(int size); + + /** + * Constructs a Stub delegating to the given implementation. + */ + protected abstract Stub<I> buildStub(Core core, I impl); + + /** + * Constructs a Proxy forwarding the calls to the given message receiver. + */ + protected abstract P buildProxy(Core core, MessageReceiverWithResponder messageReceiver); + + } +} diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceControlMessagesHelper.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceControlMessagesHelper.java new file mode 100644 index 0000000..939fb93 --- /dev/null +++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceControlMessagesHelper.java @@ -0,0 +1,89 @@ +// Copyright 2015 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. + +package org.chromium.mojo.bindings; + +import org.chromium.mojo.bindings.Callbacks.Callback1; +import org.chromium.mojo.bindings.Interface.Manager; +import org.chromium.mojo.bindings.Interface.Proxy; +import org.chromium.mojo.system.Core; + +/** + * Helper class to handle interface control messages. See + * mojo/public/interfaces/bindings/interface_control_messages.mojom. + */ +public class InterfaceControlMessagesHelper { + /** + * MessageReceiver that forwards a message containing a {@link RunResponseMessageParams} to a + * callback. + */ + private static class RunResponseForwardToCallback + extends SideEffectFreeCloseable implements MessageReceiver { + private final Callback1<RunResponseMessageParams> mCallback; + + RunResponseForwardToCallback(Callback1<RunResponseMessageParams> callback) { + mCallback = callback; + } + + /** + * @see MessageReceiver#accept(Message) + */ + @Override + public boolean accept(Message message) { + RunResponseMessageParams response = + RunResponseMessageParams.deserialize(message.asServiceMessage().getPayload()); + mCallback.call(response); + return true; + } + } + + /** + * Sends the given run message through the receiver, registering the callback. + */ + public static void sendRunMessage(Core core, MessageReceiverWithResponder receiver, + RunMessageParams params, Callback1<RunResponseMessageParams> callback) { + Message message = params.serializeWithHeader( + core, new MessageHeader(InterfaceControlMessagesConstants.RUN_MESSAGE_ID, + MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG, 0)); + receiver.acceptWithResponder(message, new RunResponseForwardToCallback(callback)); + } + + /** + * Sends the given run or close pipe message through the receiver. + */ + public static void sendRunOrClosePipeMessage( + Core core, MessageReceiverWithResponder receiver, RunOrClosePipeMessageParams params) { + Message message = params.serializeWithHeader(core, + new MessageHeader(InterfaceControlMessagesConstants.RUN_OR_CLOSE_PIPE_MESSAGE_ID)); + receiver.accept(message); + } + + /** + * Handles a received run message. + */ + public static <I extends Interface, P extends Proxy> boolean handleRun( + Core core, Manager<I, P> manager, ServiceMessage message, MessageReceiver responder) { + RunResponseMessageParams response = new RunResponseMessageParams(); + response.reserved0 = 16; + response.reserved1 = 0; + response.queryVersionResult = new QueryVersionResult(); + response.queryVersionResult.version = manager.getVersion(); + + return responder.accept(response.serializeWithHeader( + core, new MessageHeader(InterfaceControlMessagesConstants.RUN_MESSAGE_ID, + MessageHeader.MESSAGE_IS_RESPONSE_FLAG, + message.getHeader().getRequestId()))); + } + + /** + * Handles a received run or close pipe message. Closing the pipe is handled by returning + * |false|. + */ + public static <I extends Interface, P extends Proxy> boolean handleRunOrClosePipe( + Manager<I, P> manager, ServiceMessage message) { + Message payload = message.getPayload(); + RunOrClosePipeMessageParams query = RunOrClosePipeMessageParams.deserialize(payload); + return query.requireVersion.version <= manager.getVersion(); + } +} diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceRequest.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceRequest.java new file mode 100644 index 0000000..61899b4 --- /dev/null +++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceRequest.java @@ -0,0 +1,58 @@ +// 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. + +package org.chromium.mojo.bindings; + +import org.chromium.mojo.system.MessagePipeHandle; + +/** + * One end of the message pipe representing a request to create an implementation to be bound to it. + * The other end of the pipe is bound to a proxy, which can be used immediately, while the + * InterfaceRequest is being sent. + * <p> + * InterfaceRequest are built using |Interface.Manager|. + * + * @param <P> the type of the remote interface proxy. + */ +public class InterfaceRequest<P extends Interface> implements HandleOwner<MessagePipeHandle> { + + /** + * The handle which will be sent and will be connected to the implementation. + */ + private final MessagePipeHandle mHandle; + + /** + * Constructor. + * + * @param handle the handle which will be sent and will be connected to the implementation. + */ + InterfaceRequest(MessagePipeHandle handle) { + mHandle = handle; + } + + /** + * @see HandleOwner#passHandle() + */ + @Override + public MessagePipeHandle passHandle() { + return mHandle.pass(); + } + + /** + * @see java.io.Closeable#close() + */ + @Override + public void close() { + mHandle.close(); + } + + /** + * Returns an {@link InterfaceRequest} that wraps the given handle. This method is not type safe + * and should be avoided unless absolutely necessary. + */ + @SuppressWarnings("rawtypes") + public static InterfaceRequest asInterfaceRequestUnsafe(MessagePipeHandle handle) { + return new InterfaceRequest(handle); + } +} diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Message.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Message.java new file mode 100644 index 0000000..0d270cc --- /dev/null +++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Message.java @@ -0,0 +1,69 @@ +// 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. + +package org.chromium.mojo.bindings; + +import org.chromium.mojo.system.Handle; +import org.chromium.mojo.system.MessagePipeHandle; + +import java.nio.ByteBuffer; +import java.util.List; + +/** + * A raw message to be sent/received from a {@link MessagePipeHandle}. Note that this can contain + * any data, not necessarily a Mojo message with a proper header. See also {@link ServiceMessage}. + */ +public class Message { + + /** + * The data of the message. + */ + private final ByteBuffer mBuffer; + + /** + * The handles of the message. + */ + private final List<? extends Handle> mHandle; + + /** + * This message interpreted as a message for a mojo service with an appropriate header. + */ + private ServiceMessage mWithHeader = null; + + /** + * Constructor. + * + * @param buffer The buffer containing the bytes to send. This must be a direct buffer. + * @param handles The list of handles to send. + */ + public Message(ByteBuffer buffer, List<? extends Handle> handles) { + assert buffer.isDirect(); + mBuffer = buffer; + mHandle = handles; + } + + /** + * The data of the message. + */ + public ByteBuffer getData() { + return mBuffer; + } + + /** + * The handles of the message. + */ + public List<? extends Handle> getHandles() { + return mHandle; + } + + /** + * Returns the message interpreted as a message for a mojo service. + */ + public ServiceMessage asServiceMessage() { + if (mWithHeader == null) { + mWithHeader = new ServiceMessage(this); + } + return mWithHeader; + } +} diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageHeader.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageHeader.java new file mode 100644 index 0000000..771d22b --- /dev/null +++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageHeader.java @@ -0,0 +1,248 @@ +// 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. + +package org.chromium.mojo.bindings; + +import java.nio.ByteBuffer; + +/** + * Header information for a message. + */ +public class MessageHeader { + + private static final int SIMPLE_MESSAGE_SIZE = 24; + private static final int SIMPLE_MESSAGE_VERSION = 0; + private static final DataHeader SIMPLE_MESSAGE_STRUCT_INFO = + new DataHeader(SIMPLE_MESSAGE_SIZE, SIMPLE_MESSAGE_VERSION); + + private static final int MESSAGE_WITH_REQUEST_ID_SIZE = 32; + private static final int MESSAGE_WITH_REQUEST_ID_VERSION = 1; + private static final DataHeader MESSAGE_WITH_REQUEST_ID_STRUCT_INFO = + new DataHeader(MESSAGE_WITH_REQUEST_ID_SIZE, MESSAGE_WITH_REQUEST_ID_VERSION); + + private static final int INTERFACE_ID_OFFSET = 8; + private static final int TYPE_OFFSET = 12; + private static final int FLAGS_OFFSET = 16; + private static final int REQUEST_ID_OFFSET = 24; + + /** + * Flag for a header of a simple message. + */ + public static final int NO_FLAG = 0; + + /** + * Flag for a header of a message that expected a response. + */ + public static final int MESSAGE_EXPECTS_RESPONSE_FLAG = 1 << 0; + + /** + * Flag for a header of a message that is a response. + */ + public static final int MESSAGE_IS_RESPONSE_FLAG = 1 << 1; + + private final DataHeader mDataHeader; + private final int mType; + private final int mFlags; + private long mRequestId; + + /** + * Constructor for the header of a message which does not have a response. + */ + public MessageHeader(int type) { + mDataHeader = SIMPLE_MESSAGE_STRUCT_INFO; + mType = type; + mFlags = 0; + mRequestId = 0; + } + + /** + * Constructor for the header of a message which have a response or being itself a response. + */ + public MessageHeader(int type, int flags, long requestId) { + assert mustHaveRequestId(flags); + mDataHeader = MESSAGE_WITH_REQUEST_ID_STRUCT_INFO; + mType = type; + mFlags = flags; + mRequestId = requestId; + } + + /** + * Constructor, parsing the header from a message. Should only be used by {@link Message} + * itself. + */ + MessageHeader(Message message) { + Decoder decoder = new Decoder(message); + mDataHeader = decoder.readDataHeader(); + validateDataHeader(mDataHeader); + + // Currently associated interfaces are not supported. + int interfaceId = decoder.readInt(INTERFACE_ID_OFFSET); + if (interfaceId != 0) { + throw new DeserializationException("Non-zero interface ID, expecting zero since " + + "associated interfaces are not yet supported."); + } + + mType = decoder.readInt(TYPE_OFFSET); + mFlags = decoder.readInt(FLAGS_OFFSET); + if (mustHaveRequestId(mFlags)) { + if (mDataHeader.size < MESSAGE_WITH_REQUEST_ID_SIZE) { + throw new DeserializationException("Incorrect message size, expecting at least " + + MESSAGE_WITH_REQUEST_ID_SIZE + + " for a message with a request identifier, but got: " + mDataHeader.size); + + } + mRequestId = decoder.readLong(REQUEST_ID_OFFSET); + } else { + mRequestId = 0; + } + } + + /** + * Returns the size in bytes of the serialization of the header. + */ + public int getSize() { + return mDataHeader.size; + } + + /** + * Returns the type of the message. + */ + public int getType() { + return mType; + } + + /** + * Returns the flags associated to the message. + */ + public int getFlags() { + return mFlags; + } + + /** + * Returns if the message has the given flag. + */ + public boolean hasFlag(int flag) { + return (mFlags & flag) == flag; + } + + /** + * Returns if the message has a request id. + */ + public boolean hasRequestId() { + return mustHaveRequestId(mFlags); + } + + /** + * Return the request id for the message. Must only be called if the message has a request id. + */ + public long getRequestId() { + assert hasRequestId(); + return mRequestId; + } + + /** + * Encode the header. + */ + public void encode(Encoder encoder) { + encoder.encode(mDataHeader); + // Set the interface ID field to 0. + encoder.encode(0, INTERFACE_ID_OFFSET); + encoder.encode(getType(), TYPE_OFFSET); + encoder.encode(getFlags(), FLAGS_OFFSET); + if (hasRequestId()) { + encoder.encode(getRequestId(), REQUEST_ID_OFFSET); + } + } + + /** + * Returns true if the header has the expected flags. Only considers flags this class knows + * about in order to allow this class to work with future version of the header format. + */ + public boolean validateHeader(int expectedFlags) { + int knownFlags = getFlags() & (MESSAGE_EXPECTS_RESPONSE_FLAG | MESSAGE_IS_RESPONSE_FLAG); + return knownFlags == expectedFlags; + } + + /** + * Returns true if the header has the expected type and flags. Only consider flags this class + * knows about in order to allow this class to work with future version of the header format. + */ + public boolean validateHeader(int expectedType, int expectedFlags) { + return getType() == expectedType && validateHeader(expectedFlags); + } + + /** + * @see Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((mDataHeader == null) ? 0 : mDataHeader.hashCode()); + result = prime * result + mFlags; + result = prime * result + (int) (mRequestId ^ (mRequestId >>> 32)); + result = prime * result + mType; + return result; + } + + /** + * @see Object#equals(Object) + */ + @Override + public boolean equals(Object object) { + if (object == this) return true; + if (object == null) return false; + if (getClass() != object.getClass()) return false; + + MessageHeader other = (MessageHeader) object; + return (BindingsHelper.equals(mDataHeader, other.mDataHeader) + && mFlags == other.mFlags + && mRequestId == other.mRequestId + && mType == other.mType); + } + + /** + * Set the request id on the message contained in the given buffer. + */ + void setRequestId(ByteBuffer buffer, long requestId) { + assert mustHaveRequestId(buffer.getInt(FLAGS_OFFSET)); + buffer.putLong(REQUEST_ID_OFFSET, requestId); + mRequestId = requestId; + } + + /** + * Returns whether a message with the given flags must have a request Id. + */ + private static boolean mustHaveRequestId(int flags) { + return (flags & (MESSAGE_EXPECTS_RESPONSE_FLAG | MESSAGE_IS_RESPONSE_FLAG)) != 0; + } + + /** + * Validate that the given {@link DataHeader} can be the data header of a message header. + */ + private static void validateDataHeader(DataHeader dataHeader) { + if (dataHeader.elementsOrVersion < SIMPLE_MESSAGE_VERSION) { + throw new DeserializationException("Incorrect number of fields, expecting at least " + + SIMPLE_MESSAGE_VERSION + ", but got: " + dataHeader.elementsOrVersion); + } + if (dataHeader.size < SIMPLE_MESSAGE_SIZE) { + throw new DeserializationException( + "Incorrect message size, expecting at least " + SIMPLE_MESSAGE_SIZE + + ", but got: " + dataHeader.size); + } + if (dataHeader.elementsOrVersion == SIMPLE_MESSAGE_VERSION + && dataHeader.size != SIMPLE_MESSAGE_SIZE) { + throw new DeserializationException("Incorrect message size for a message with " + + SIMPLE_MESSAGE_VERSION + " fields, expecting " + SIMPLE_MESSAGE_SIZE + + ", but got: " + dataHeader.size); + } + if (dataHeader.elementsOrVersion == MESSAGE_WITH_REQUEST_ID_VERSION + && dataHeader.size != MESSAGE_WITH_REQUEST_ID_SIZE) { + throw new DeserializationException("Incorrect message size for a message with " + + MESSAGE_WITH_REQUEST_ID_VERSION + " fields, expecting " + + MESSAGE_WITH_REQUEST_ID_SIZE + ", but got: " + dataHeader.size); + } + } + +} diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiver.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiver.java new file mode 100644 index 0000000..4223297 --- /dev/null +++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiver.java @@ -0,0 +1,25 @@ +// 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. + +package org.chromium.mojo.bindings; + +import java.io.Closeable; + +/** + * A class which implements this interface can receive {@link Message} objects. + */ +public interface MessageReceiver extends Closeable { + + /** + * Receive a {@link Message}. The {@link MessageReceiver} is allowed to mutate the message. + * Returns |true| if the message has been handled, |false| otherwise. + */ + boolean accept(Message message); + + /** + * @see java.io.Closeable#close() + */ + @Override + public void close(); +} diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiverWithResponder.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiverWithResponder.java new file mode 100644 index 0000000..e24a558 --- /dev/null +++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiverWithResponder.java @@ -0,0 +1,21 @@ +// 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. + +package org.chromium.mojo.bindings; + +/** + * A {@link MessageReceiver} that can also handle the handle the response message generated from the + * given message. + */ +public interface MessageReceiverWithResponder extends MessageReceiver { + + /** + * A variant on {@link #accept(Message)} that registers a {@link MessageReceiver} + * (known as the responder) to handle the response message generated from the given message. The + * responder's {@link #accept(Message)} method may be called as part of the call to + * {@link #acceptWithResponder(Message, MessageReceiver)}, or some time after its + * return. + */ + boolean acceptWithResponder(Message message, MessageReceiver responder); +} diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Router.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Router.java new file mode 100644 index 0000000..ba19ae5 --- /dev/null +++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Router.java @@ -0,0 +1,31 @@ +// 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. + +package org.chromium.mojo.bindings; + +import org.chromium.mojo.system.MessagePipeHandle; + +/** + * A {@link Router} will handle mojo message and forward those to a {@link Connector}. It deals with + * parsing of headers and adding of request ids in order to be able to match a response to a + * request. + */ +public interface Router extends MessageReceiverWithResponder, HandleOwner<MessagePipeHandle> { + + /** + * Start listening for incoming messages. + */ + public void start(); + + /** + * Set the {@link MessageReceiverWithResponder} that will deserialize and use the message + * received from the pipe. + */ + public void setIncomingMessageReceiver(MessageReceiverWithResponder incomingMessageReceiver); + + /** + * Set the handle that will be notified of errors on the message pipe. + */ + public void setErrorHandler(ConnectionErrorHandler errorHandler); +} diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/RouterImpl.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/RouterImpl.java new file mode 100644 index 0000000..815d79e --- /dev/null +++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/RouterImpl.java @@ -0,0 +1,264 @@ +// 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. + +package org.chromium.mojo.bindings; + +import org.chromium.mojo.system.AsyncWaiter; +import org.chromium.mojo.system.Core; +import org.chromium.mojo.system.MessagePipeHandle; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Executor; + +/** + * Implementation of {@link Router}. + */ +public class RouterImpl implements Router { + + /** + * {@link MessageReceiver} used as the {@link Connector} callback. + */ + private class HandleIncomingMessageThunk implements MessageReceiver { + + /** + * @see MessageReceiver#accept(Message) + */ + @Override + public boolean accept(Message message) { + return handleIncomingMessage(message); + } + + /** + * @see MessageReceiver#close() + */ + @Override + public void close() { + handleConnectorClose(); + } + + } + + /** + * + * {@link MessageReceiver} used to return responses to the caller. + */ + class ResponderThunk implements MessageReceiver { + private boolean mAcceptWasInvoked = false; + + /** + * @see + * MessageReceiver#accept(Message) + */ + @Override + public boolean accept(Message message) { + mAcceptWasInvoked = true; + return RouterImpl.this.accept(message); + } + + /** + * @see MessageReceiver#close() + */ + @Override + public void close() { + RouterImpl.this.close(); + } + + @Override + protected void finalize() throws Throwable { + if (!mAcceptWasInvoked) { + // We close the pipe here as a way of signaling to the calling application that an + // error condition occurred. Without this the calling application would have no + // way of knowing it should stop waiting for a response. + RouterImpl.this.closeOnHandleThread(); + } + super.finalize(); + } + } + + /** + * The {@link Connector} which is connected to the handle. + */ + private final Connector mConnector; + + /** + * The {@link MessageReceiverWithResponder} that will consume the messages received from the + * pipe. + */ + private MessageReceiverWithResponder mIncomingMessageReceiver; + + /** + * The next id to use for a request id which needs a response. It is auto-incremented. + */ + private long mNextRequestId = 1; + + /** + * The map from request ids to {@link MessageReceiver} of request currently in flight. + */ + private Map<Long, MessageReceiver> mResponders = new HashMap<Long, MessageReceiver>(); + + /** + * An Executor that will run on the thread associated with the MessagePipe to which + * this Router is bound. This may be {@code Null} if the MessagePipeHandle passed + * in to the constructor is not valid. + */ + private final Executor mExecutor; + + /** + * Constructor that will use the default {@link AsyncWaiter}. + * + * @param messagePipeHandle The {@link MessagePipeHandle} to route message for. + */ + public RouterImpl(MessagePipeHandle messagePipeHandle) { + this(messagePipeHandle, BindingsHelper.getDefaultAsyncWaiterForHandle(messagePipeHandle)); + } + + /** + * Constructor. + * + * @param messagePipeHandle The {@link MessagePipeHandle} to route message for. + * @param asyncWaiter the {@link AsyncWaiter} to use to get notification of new messages on the + * handle. + */ + public RouterImpl(MessagePipeHandle messagePipeHandle, AsyncWaiter asyncWaiter) { + mConnector = new Connector(messagePipeHandle, asyncWaiter); + mConnector.setIncomingMessageReceiver(new HandleIncomingMessageThunk()); + Core core = messagePipeHandle.getCore(); + if (core != null) { + mExecutor = ExecutorFactory.getExecutorForCurrentThread(core); + } else { + mExecutor = null; + } + } + + /** + * @see org.chromium.mojo.bindings.Router#start() + */ + @Override + public void start() { + mConnector.start(); + } + + /** + * @see Router#setIncomingMessageReceiver(MessageReceiverWithResponder) + */ + @Override + public void setIncomingMessageReceiver(MessageReceiverWithResponder incomingMessageReceiver) { + this.mIncomingMessageReceiver = incomingMessageReceiver; + } + + /** + * @see MessageReceiver#accept(Message) + */ + @Override + public boolean accept(Message message) { + // A message without responder is directly forwarded to the connector. + return mConnector.accept(message); + } + + /** + * @see MessageReceiverWithResponder#acceptWithResponder(Message, MessageReceiver) + */ + @Override + public boolean acceptWithResponder(Message message, MessageReceiver responder) { + // The message must have a header. + ServiceMessage messageWithHeader = message.asServiceMessage(); + // Checking the message expects a response. + assert messageWithHeader.getHeader().hasFlag(MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG); + + // Compute a request id for being able to route the response. + long requestId = mNextRequestId++; + // Reserve 0 in case we want it to convey special meaning in the future. + if (requestId == 0) { + requestId = mNextRequestId++; + } + if (mResponders.containsKey(requestId)) { + throw new IllegalStateException("Unable to find a new request identifier."); + } + messageWithHeader.setRequestId(requestId); + if (!mConnector.accept(messageWithHeader)) { + return false; + } + // Only keep the responder is the message has been accepted. + mResponders.put(requestId, responder); + return true; + } + + /** + * @see org.chromium.mojo.bindings.HandleOwner#passHandle() + */ + @Override + public MessagePipeHandle passHandle() { + return mConnector.passHandle(); + } + + /** + * @see java.io.Closeable#close() + */ + @Override + public void close() { + mConnector.close(); + } + + /** + * @see Router#setErrorHandler(ConnectionErrorHandler) + */ + @Override + public void setErrorHandler(ConnectionErrorHandler errorHandler) { + mConnector.setErrorHandler(errorHandler); + } + + /** + * Receive a message from the connector. Returns |true| if the message has been handled. + */ + private boolean handleIncomingMessage(Message message) { + MessageHeader header = message.asServiceMessage().getHeader(); + if (header.hasFlag(MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG)) { + if (mIncomingMessageReceiver != null) { + return mIncomingMessageReceiver.acceptWithResponder(message, new ResponderThunk()); + } + // If we receive a request expecting a response when the client is not + // listening, then we have no choice but to tear down the pipe. + close(); + return false; + } else if (header.hasFlag(MessageHeader.MESSAGE_IS_RESPONSE_FLAG)) { + long requestId = header.getRequestId(); + MessageReceiver responder = mResponders.get(requestId); + if (responder == null) { + return false; + } + mResponders.remove(requestId); + return responder.accept(message); + } else { + if (mIncomingMessageReceiver != null) { + return mIncomingMessageReceiver.accept(message); + } + // OK to drop the message. + } + return false; + } + + private void handleConnectorClose() { + if (mIncomingMessageReceiver != null) { + mIncomingMessageReceiver.close(); + } + } + + /** + * Invokes {@link #close()} asynchronously on the thread associated with + * this Router's Handle. If this Router was constructed with an invalid + * handle then this method does nothing. + */ + private void closeOnHandleThread() { + if (mExecutor != null) { + mExecutor.execute(new Runnable() { + + @Override + public void run() { + close(); + } + }); + } + } +} diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/SerializationException.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/SerializationException.java new file mode 100644 index 0000000..d4f5502 --- /dev/null +++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/SerializationException.java @@ -0,0 +1,26 @@ +// 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. + +package org.chromium.mojo.bindings; + +/** + * Error that can be thrown when serializing a mojo message. + */ +public class SerializationException extends RuntimeException { + + /** + * Constructs a new serialization exception with the specified detail message. + */ + public SerializationException(String message) { + super(message); + } + + /** + * Constructs a new serialization exception with the specified cause. + */ + public SerializationException(Exception cause) { + super(cause); + } + +} diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ServiceMessage.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ServiceMessage.java new file mode 100644 index 0000000..313dc6a --- /dev/null +++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ServiceMessage.java @@ -0,0 +1,73 @@ +// 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. + +package org.chromium.mojo.bindings; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * Represents a {@link Message} which contains a {@link MessageHeader}. Deals with parsing the + * {@link MessageHeader} for a message. + */ +public class ServiceMessage extends Message { + + private final MessageHeader mHeader; + private Message mPayload; + + /** + * Reinterpret the given |message| as a message with the given |header|. The |message| must + * contain the |header| as the start of its raw data. + */ + public ServiceMessage(Message baseMessage, MessageHeader header) { + super(baseMessage.getData(), baseMessage.getHandles()); + assert header.equals(new org.chromium.mojo.bindings.MessageHeader(baseMessage)); + this.mHeader = header; + } + + /** + * Reinterpret the given |message| as a message with a header. The |message| must contain a + * header as the start of it's raw data, which will be parsed by this constructor. + */ + ServiceMessage(Message baseMessage) { + this(baseMessage, new org.chromium.mojo.bindings.MessageHeader(baseMessage)); + } + + /** + * @see Message#asServiceMessage() + */ + @Override + public ServiceMessage asServiceMessage() { + return this; + } + + /** + * Returns the header of the given message. This will throw a {@link DeserializationException} + * if the start of the message is not a valid header. + */ + public MessageHeader getHeader() { + return mHeader; + } + + /** + * Returns the payload of the message. + */ + public Message getPayload() { + if (mPayload == null) { + ByteBuffer truncatedBuffer = + ((ByteBuffer) getData().position(getHeader().getSize())).slice(); + truncatedBuffer.order(ByteOrder.LITTLE_ENDIAN); + mPayload = new Message(truncatedBuffer, getHandles()); + } + return mPayload; + } + + /** + * Set the request identifier on the message. + */ + void setRequestId(long requestId) { + mHeader.setRequestId(getData(), requestId); + } + +} diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/SideEffectFreeCloseable.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/SideEffectFreeCloseable.java new file mode 100644 index 0000000..118c991 --- /dev/null +++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/SideEffectFreeCloseable.java @@ -0,0 +1,21 @@ +// 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. + +package org.chromium.mojo.bindings; + +import java.io.Closeable; + +/** + * An implementation of closeable that doesn't do anything. + */ +public class SideEffectFreeCloseable implements Closeable { + + /** + * @see java.io.Closeable#close() + */ + @Override + public void close() { + } + +} diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Struct.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Struct.java new file mode 100644 index 0000000..85cc97c --- /dev/null +++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Struct.java @@ -0,0 +1,69 @@ +// 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. + +package org.chromium.mojo.bindings; + +import org.chromium.mojo.system.Core; + +/** + * Base class for all mojo structs. + */ +public abstract class Struct { + /** + * The base size of the encoded struct. + */ + private final int mEncodedBaseSize; + + /** + * The version of the struct. + */ + private final int mVersion; + + /** + * Constructor. + */ + protected Struct(int encodedBaseSize, int version) { + mEncodedBaseSize = encodedBaseSize; + mVersion = version; + } + + /** + * Returns the version of the struct. It is the max version of the struct in the mojom if it has + * been created locally, and the version of the received struct if it has been deserialized. + */ + public int getVersion() { + return mVersion; + } + + /** + * Returns the serialization of the struct. This method can close Handles. + * + * @param core the |Core| implementation used to generate handles. Only used if the data + * structure being encoded contains interfaces, can be |null| otherwise. + */ + public Message serialize(Core core) { + Encoder encoder = new Encoder(core, mEncodedBaseSize); + encode(encoder); + return encoder.getMessage(); + } + + /** + * Returns the serialization of the struct prepended with the given header. + * + * @param header the header to prepend to the returned message. + * @param core the |Core| implementation used to generate handles. Only used if the |Struct| + * being encoded contains interfaces, can be |null| otherwise. + */ + public ServiceMessage serializeWithHeader(Core core, MessageHeader header) { + Encoder encoder = new Encoder(core, mEncodedBaseSize + header.getSize()); + header.encode(encoder); + encode(encoder); + return new ServiceMessage(encoder.getMessage(), header); + } + + /** + * Use the given encoder to serialize this data structure. + */ + protected abstract void encode(Encoder encoder); +} diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Union.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Union.java new file mode 100644 index 0000000..90b40ea --- /dev/null +++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Union.java @@ -0,0 +1,30 @@ +// 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. + +package org.chromium.mojo.bindings; + +import org.chromium.mojo.system.Core; + +/** + * Base class for all mojo unions. + */ +public abstract class Union { + /** + * Returns the serialization of the union. This method can close Handles. + * + * @param core the |Core| implementation used to generate handles. Only used if the data + * structure being encoded contains interfaces, can be |null| otherwise. + */ + public Message serialize(Core core) { + Encoder encoder = new Encoder(core, BindingsHelper.UNION_SIZE); + encoder.claimMemory(16); + encode(encoder, 0); + return encoder.getMessage(); + } + + /** + * Serializes this data structure using the given encoder. + */ + protected abstract void encode(Encoder encoder, int offset); +} diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/AsyncWaiter.java b/mojo/public/java/system/src/org/chromium/mojo/system/AsyncWaiter.java new file mode 100644 index 0000000..474de4b --- /dev/null +++ b/mojo/public/java/system/src/org/chromium/mojo/system/AsyncWaiter.java @@ -0,0 +1,53 @@ +// 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. + +package org.chromium.mojo.system; + +import org.chromium.mojo.system.Core.HandleSignals; + +/** + * A class which implements the {@link AsyncWaiter} allows asynchronously waiting on a background + * thread. + */ +public interface AsyncWaiter { + + /** + * Allows cancellation of an asyncWait operation. + */ + interface Cancellable { + /** + * Cancels an asyncWait operation. Has no effect if the operation has already been canceled + * or the callback has already been called. + * <p> + * Must be called from the same thread as {@link AsyncWaiter#asyncWait} was called from. + */ + void cancel(); + } + + /** + * Callback passed to {@link AsyncWaiter#asyncWait}. + */ + public interface Callback { + /** + * Called when the handle is ready. + */ + public void onResult(int result); + + /** + * Called when an error occurred while waiting. + */ + public void onError(MojoException exception); + } + + /** + * Asynchronously call wait on a background thread. The given {@link Callback} will be notified + * of the result of the wait on the same thread as asyncWait was called. + * + * @return a {@link Cancellable} object that can be used to cancel waiting. The cancellable + * should only be used on the current thread, and becomes invalid once the callback has + * been notified. + */ + Cancellable asyncWait(Handle handle, HandleSignals signals, long deadline, Callback callback); + +} diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/Core.java b/mojo/public/java/system/src/org/chromium/mojo/system/Core.java new file mode 100644 index 0000000..ba0e5c6 --- /dev/null +++ b/mojo/public/java/system/src/org/chromium/mojo/system/Core.java @@ -0,0 +1,321 @@ +// 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. + +package org.chromium.mojo.system; + +import java.util.List; + +/** + * Core mojo interface giving access to the base operations. See |src/mojo/public/c/system/core.h| + * for the underlying api. + */ +public interface Core { + + /** + * Used to indicate an infinite deadline (timeout). + */ + public static final long DEADLINE_INFINITE = -1; + + /** + * Signals for the wait operations on handles. + */ + public static class HandleSignals extends Flags<HandleSignals> { + /** + * Constructor. + * + * @param signals the serialized signals. + */ + public HandleSignals(int signals) { + super(signals); + } + + private static final int FLAG_NONE = 0; + private static final int FLAG_READABLE = 1 << 0; + private static final int FLAG_WRITABLE = 1 << 1; + private static final int FLAG_PEER_CLOSED = 1 << 2; + + /** + * Immutable signals. + */ + public static final HandleSignals NONE = HandleSignals.none().immutable(); + public static final HandleSignals READABLE = + HandleSignals.none().setReadable(true).immutable(); + public static final HandleSignals WRITABLE = + HandleSignals.none().setWritable(true).immutable(); + + /** + * Change the readable bit of this signal. + * + * @param readable the new value of the readable bit. + * @return this. + */ + public HandleSignals setReadable(boolean readable) { + return setFlag(FLAG_READABLE, readable); + } + + /** + * Change the writable bit of this signal. + * + * @param writable the new value of the writable bit. + * @return this. + */ + public HandleSignals setWritable(boolean writable) { + return setFlag(FLAG_WRITABLE, writable); + } + + /** + * Change the peer closed bit of this signal. + * + * @param peerClosed the new value of the peer closed bit. + * @return this. + */ + public HandleSignals setPeerClosed(boolean peerClosed) { + return setFlag(FLAG_PEER_CLOSED, peerClosed); + } + + /** + * Returns a signal with no bit set. + */ + public static HandleSignals none() { + return new HandleSignals(FLAG_NONE); + } + + } + + /** + * Returns a platform-dependent monotonically increasing tick count representing "right now." + */ + public long getTimeTicksNow(); + + /** + * Returned by wait functions to indicate the signaling state of handles. + */ + public static class HandleSignalsState { + /** + * Signals that were satisfied at some time // before the call returned. + */ + private final HandleSignals mSatisfiedSignals; + + /** + * Signals that are possible to satisfy. For example, if the return value was + * |MOJO_RESULT_FAILED_PRECONDITION|, you can use this field to determine which, if any, of + * the signals can still be satisfied. + */ + private final HandleSignals mSatisfiableSignals; + + /** + * Constructor. + */ + public HandleSignalsState( + HandleSignals satisfiedSignals, HandleSignals satisfiableSignals) { + mSatisfiedSignals = satisfiedSignals; + mSatisfiableSignals = satisfiableSignals; + } + + /** + * Returns the satisfiedSignals. + */ + public HandleSignals getSatisfiedSignals() { + return mSatisfiedSignals; + } + + /** + * Returns the satisfiableSignals. + */ + public HandleSignals getSatisfiableSignals() { + return mSatisfiableSignals; + } + } + + /** + * Result for the |wait| method. + */ + public static class WaitResult { + /** + * The result of the wait method. + * <p> + * |MojoResult.OK| if some signal in |signals| was satisfied (or is already satisfied). + * <p> + * |MojoResult.DEADLINE_EXCEEDED| if the deadline has passed without any of the signals + * being satisfied. + * <p> + * |MojoResult.CANCELLED| if |handle| is closed concurrently by another thread. + * <p> + * |MojoResult.FAILED_PRECONDITION| if it is or becomes impossible that any flag in + * |signals| will ever be satisfied (for example, if the other endpoint is closed). + */ + private int mMojoResult; + + /** + * The signaling state of handles. + */ + private HandleSignalsState mHandleSignalsState; + + /** + * Returns the mojoResult. + */ + public int getMojoResult() { + return mMojoResult; + } + + /** + * @param mojoResult the mojoResult to set + */ + public void setMojoResult(int mojoResult) { + mMojoResult = mojoResult; + } + + /** + * Returns the handleSignalsState. + */ + public HandleSignalsState getHandleSignalsState() { + return mHandleSignalsState; + } + + /** + * @param handleSignalsState the handleSignalsState to set + */ + public void setHandleSignalsState(HandleSignalsState handleSignalsState) { + mHandleSignalsState = handleSignalsState; + } + } + + /** + * Waits on the given |handle| until the state indicated by |signals| is satisfied or until + * |deadline| has passed. + * + * @return a |WaitResult|. + */ + public WaitResult wait(Handle handle, HandleSignals signals, long deadline); + + /** + * Result for the |waitMany| method. + */ + public static class WaitManyResult { + + /** + * See |wait| for the different possible values. + */ + private int mMojoResult; + + /** + * If |mojoResult| is |MojoResult.OK|, |handleIndex| is the index of the handle for which + * some flag was satisfied (or is already satisfied). If |mojoResult| is + * |MojoResult.CANCELLED| or |MojoResult.FAILED_PRECONDITION|, |handleIndex| is the index of + * the handle for which the issue occurred. + */ + private int mHandleIndex; + + /** + * The signaling state of handles. Will not be set if |mojoResult| is + * |MOJO_RESULT_INVALID_ARGUMENT| or |MOJO_RESULT_RESOURCE_EXHAUSTED| + */ + private List<HandleSignalsState> mSignalStates; + + /** + * Returns the mojoResult. + */ + public int getMojoResult() { + return mMojoResult; + } + + /** + * @param mojoResult the mojoResult to set + */ + public void setMojoResult(int mojoResult) { + mMojoResult = mojoResult; + } + + /** + * Returns the handleIndex. + */ + public int getHandleIndex() { + return mHandleIndex; + } + + /** + * @param handleIndex the handleIndex to set + */ + public void setHandleIndex(int handleIndex) { + mHandleIndex = handleIndex; + } + + /** + * Returns the signalStates. + */ + public List<HandleSignalsState> getSignalStates() { + return mSignalStates; + } + + /** + * @param signalStates the signalStates to set + */ + public void setSignalStates(List<HandleSignalsState> signalStates) { + mSignalStates = signalStates; + } + } + + /** + * Waits on handle in |handles| for at least one of them to satisfy the associated + * |HandleSignals|, or until |deadline| has passed. + * + * @returns a |WaitManyResult|. + */ + public WaitManyResult waitMany(List<Pair<Handle, HandleSignals>> handles, long deadline); + + /** + * Creates a message pipe, which is a bidirectional communication channel for framed data (i.e., + * messages), with the given options. Messages can contain plain data and/or Mojo handles. + * + * @return the set of handles for the two endpoints (ports) of the message pipe. + */ + public Pair<MessagePipeHandle, MessagePipeHandle> createMessagePipe( + MessagePipeHandle.CreateOptions options); + + /** + * Creates a data pipe, which is a unidirectional communication channel for unframed data, with + * the given options. Data is unframed, but must come as (multiples of) discrete elements, of + * the size given in |options|. See |DataPipe.CreateOptions| for a description of the different + * options available for data pipes. |options| may be set to null for a data pipe with the + * default options (which will have an element size of one byte and have some system-dependent + * capacity). + * + * @return the set of handles for the two endpoints of the data pipe. + */ + public Pair<DataPipe.ProducerHandle, DataPipe.ConsumerHandle> createDataPipe( + DataPipe.CreateOptions options); + + /** + * Creates a buffer that can be shared between applications (by duplicating the handle -- see + * |SharedBufferHandle.duplicate()| -- and passing it over a message pipe). To access the + * buffer, one must call |SharedBufferHandle.map|. + * + * @return the new |SharedBufferHandle|. + */ + public SharedBufferHandle createSharedBuffer(SharedBufferHandle.CreateOptions options, + long numBytes); + + /** + * Acquires a handle from the native side. The handle will be owned by the returned object and + * must not be closed outside of it. + * + * @return a new {@link UntypedHandle} representing the native handle. + */ + public UntypedHandle acquireNativeHandle(int handle); + + /** + * Returns a default implementation of {@link AsyncWaiter}. + */ + public AsyncWaiter getDefaultAsyncWaiter(); + + /** + * Returns a new run loop. + */ + public RunLoop createDefaultRunLoop(); + + /** + * Returns the current run loop if it exists. + */ + public RunLoop getCurrentRunLoop(); +} diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/DataPipe.java b/mojo/public/java/system/src/org/chromium/mojo/system/DataPipe.java new file mode 100644 index 0000000..4deaf09 --- /dev/null +++ b/mojo/public/java/system/src/org/chromium/mojo/system/DataPipe.java @@ -0,0 +1,334 @@ +// 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. + +package org.chromium.mojo.system; + +import java.nio.ByteBuffer; + +/** + * Interface for data pipes. A data pipe is a unidirectional communication channel for unframed + * data. Data is unframed, but must come as (multiples of) discrete elements, of the size given at + * creation time. + */ +public interface DataPipe { + + /** + * Flags for the data pipe creation operation. + */ + public static class CreateFlags extends Flags<CreateFlags> { + private static final int FLAG_NONE = 0; + + /** + * Immutable flag with not bit set. + */ + public static final CreateFlags NONE = CreateFlags.none().immutable(); + + /** + * Dedicated constructor. + * + * @param flags initial value of the flags. + */ + protected CreateFlags(int flags) { + super(flags); + } + + /** + * @return flags with no bit set. + */ + public static CreateFlags none() { + return new CreateFlags(FLAG_NONE); + } + + } + + /** + * Used to specify creation parameters for a data pipe to |Core.createDataPipe()|. + */ + public static class CreateOptions { + + /** + * Used to specify different modes of operation, see |DataPipe.CreateFlags|. + */ + private CreateFlags mFlags = CreateFlags.none(); + /** + * The size of an element, in bytes. All transactions and buffers will consist of an + * integral number of elements. Must be nonzero. + */ + private int mElementNumBytes; + /** + * The capacity of the data pipe, in number of bytes; must be a multiple of + * |element_num_bytes|. The data pipe will always be able to queue AT LEAST this much data. + * Set to zero to opt for a system-dependent automatically-calculated capacity (which will + * always be at least one element). + */ + private int mCapacityNumBytes; + + /** + * @return the flags + */ + public CreateFlags getFlags() { + return mFlags; + } + + /** + * @return the elementNumBytes + */ + public int getElementNumBytes() { + return mElementNumBytes; + } + + /** + * @param elementNumBytes the elementNumBytes to set + */ + public void setElementNumBytes(int elementNumBytes) { + mElementNumBytes = elementNumBytes; + } + + /** + * @return the capacityNumBytes + */ + public int getCapacityNumBytes() { + return mCapacityNumBytes; + } + + /** + * @param capacityNumBytes the capacityNumBytes to set + */ + public void setCapacityNumBytes(int capacityNumBytes) { + mCapacityNumBytes = capacityNumBytes; + } + + } + + /** + * Flags for the write operations on MessagePipeHandle . + */ + public static class WriteFlags extends Flags<WriteFlags> { + private static final int FLAG_NONE = 0; + private static final int FLAG_ALL_OR_NONE = 1 << 0; + + /** + * Immutable flag with not bit set. + */ + public static final WriteFlags NONE = WriteFlags.none().immutable(); + + /** + * Dedicated constructor. + * + * @param flags initial value of the flags. + */ + private WriteFlags(int flags) { + super(flags); + } + + /** + * Change the all-or-none bit of those flags. If set, write either all the elements + * requested or none of them. + * + * @param allOrNone the new value of all-or-none bit. + * @return this. + */ + public WriteFlags setAllOrNone(boolean allOrNone) { + return setFlag(FLAG_ALL_OR_NONE, allOrNone); + } + + /** + * @return a flag with no bit set. + */ + public static WriteFlags none() { + return new WriteFlags(FLAG_NONE); + } + } + + /** + * Flags for the read operations on MessagePipeHandle. + */ + public static class ReadFlags extends Flags<ReadFlags> { + private static final int FLAG_NONE = 0; + private static final int FLAG_ALL_OR_NONE = 1 << 0; + private static final int FLAG_QUERY = 1 << 2; + private static final int FLAG_PEEK = 1 << 3; + + /** + * Immutable flag with not bit set. + */ + public static final ReadFlags NONE = ReadFlags.none().immutable(); + + /** + * Dedicated constructor. + * + * @param flags initial value of the flag. + */ + private ReadFlags(int flags) { + super(flags); + } + + /** + * Change the all-or-none bit of this flag. If set, read (or discard) either the requested + * number of elements or none. + * + * @param allOrNone the new value of the all-or-none bit. + * @return this. + */ + public ReadFlags setAllOrNone(boolean allOrNone) { + return setFlag(FLAG_ALL_OR_NONE, allOrNone); + } + + /** + * Change the query bit of this flag. If set query the number of elements available to read. + * Mutually exclusive with |discard| and |allOrNone| is ignored if this flag is set. + * + * @param query the new value of the query bit. + * @return this. + */ + public ReadFlags query(boolean query) { + return setFlag(FLAG_QUERY, query); + } + + /** + * Change the peek bit of this flag. If set, read the requested number of elements, and + * leave those elements in the pipe. A later read will return the same data. + * Mutually exclusive with |discard| and |query|. + * + * @param peek the new value of the peek bit. + * @return this. + */ + public ReadFlags peek(boolean peek) { + return setFlag(FLAG_PEEK, peek); + } + + /** + * @return a flag with no bit set. + */ + public static ReadFlags none() { + return new ReadFlags(FLAG_NONE); + } + + } + + /** + * Handle for the producer part of a data pipe. + */ + public static interface ProducerHandle extends Handle { + + /** + * @see org.chromium.mojo.system.Handle#pass() + */ + @Override + public ProducerHandle pass(); + + /** + * Writes the given data to the data pipe producer. |elements| points to data; the buffer + * must be a direct ByteBuffer and the limit should be a multiple of the data pipe's element + * size. If |allOrNone| is set in |flags|, either all the data will be written or none is. + * <p> + * On success, returns the amount of data that was actually written. + * <p> + * Note: If the data pipe has the "may discard" option flag (specified on creation), this + * will discard as much data as required to write the given data, starting with the earliest + * written data that has not been consumed. However, even with "may discard", if the buffer + * limit is greater than the data pipe's capacity (and |allOrNone| is not set), this will + * write the maximum amount possible (namely, the data pipe's capacity) and return that + * amount. It will *not* discard data from |elements|. + * + * @return number of written bytes. + */ + public ResultAnd<Integer> writeData(ByteBuffer elements, WriteFlags flags); + + /** + * Begins a two-phase write to the data pipe producer . On success, returns a |ByteBuffer| + * to which the caller can write. If flags has |allOrNone| set, then the buffer capacity + * will be at least as large as |numBytes|, which must also be a multiple of the element + * size (if |allOrNone| is not set, |numBytes| is ignored and the caller must check the + * capacity of the buffer). + * <p> + * During a two-phase write, this handle is *not* writable. E.g., if another thread tries to + * write to it, it will throw a |MojoException| with code |MojoResult.BUSY|; that thread can + * then wait for this handle to become writable again. + * <p> + * Once the caller has finished writing data to the buffer, it should call |endWriteData()| + * to specify the amount written and to complete the two-phase write. + * <p> + * Note: If the data pipe has the "may discard" option flag (specified on creation) and + * |flags| has |allOrNone| set, this may discard some data. + * + * @return The buffer to write to. + */ + public ByteBuffer beginWriteData(int numBytes, WriteFlags flags); + + /** + * Ends a two-phase write to the data pipe producer that was begun by a call to + * |beginWriteData()| on the same handle. |numBytesWritten| should indicate the amount of + * data actually written; it must be less than or equal to the capacity of the buffer + * returned by |beginWriteData()| and must be a multiple of the element size. The buffer + * returned from |beginWriteData()| must have been filled with exactly |numBytesWritten| + * bytes of data. + * <p> + * On failure, the two-phase write (if any) is ended (so the handle may become writable + * again, if there's space available) but no data written to the buffer is "put into" the + * data pipe. + */ + public void endWriteData(int numBytesWritten); + } + + /** + * Handle for the consumer part of a data pipe. + */ + public static interface ConsumerHandle extends Handle { + /** + * @see org.chromium.mojo.system.Handle#pass() + */ + @Override + public ConsumerHandle pass(); + + /** + * Discards data on the data pie consumer. This method discards up to |numBytes| (which + * again be a multiple of the element size) bytes of data, returning the amount actually + * discarded. if |flags| has |allOrNone|, it will either discard exactly |numBytes| bytes of + * data or none. In this case, |query| must not be set. + */ + public int discardData(int numBytes, ReadFlags flags); + + /** + * Reads data from the data pipe consumer. May also be used to query the amount of data + * available. If |flags| has not |query| set, this tries to read up to |elements| capacity + * (which must be a multiple of the data pipe's element size) bytes of data to |elements| + * and returns the amount actually read. |elements| must be a direct ByteBuffer. If flags + * has |allOrNone| set, it will either read exactly |elements| capacity bytes of data or + * none. + * <p> + * If flags has |query| set, it queries the amount of data available, returning the number + * of bytes available. In this case |allOrNone| is ignored, as are |elements|. + */ + public ResultAnd<Integer> readData(ByteBuffer elements, ReadFlags flags); + + /** + * Begins a two-phase read from the data pipe consumer. On success, returns a |ByteBuffer| + * from which the caller can read up to its limit bytes of data. If flags has |allOrNone| + * set, then the limit will be at least as large as |numBytes|, which must also be a + * multiple of the element size (if |allOrNone| is not set, |numBytes| is ignored). |flags| + * must not have |query| set. + * <p> + * During a two-phase read, this handle is *not* readable. E.g., if another thread tries to + * read from it, it will throw a |MojoException| with code |MojoResult.BUSY|; that thread + * can then wait for this handle to become readable again. + * <p> + * Once the caller has finished reading data from the buffer, it should call |endReadData()| + * to specify the amount read and to complete the two-phase read. + */ + public ByteBuffer beginReadData(int numBytes, ReadFlags flags); + + /** + * Ends a two-phase read from the data pipe consumer that was begun by a call to + * |beginReadData()| on the same handle. |numBytesRead| should indicate the amount of data + * actually read; it must be less than or equal to the limit of the buffer returned by + * |beginReadData()| and must be a multiple of the element size. + * <p> + * On failure, the two-phase read (if any) is ended (so the handle may become readable + * again) but no data is "removed" from the data pipe. + */ + public void endReadData(int numBytesRead); + } + +} diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/Flags.java b/mojo/public/java/system/src/org/chromium/mojo/system/Flags.java new file mode 100644 index 0000000..30ff07f --- /dev/null +++ b/mojo/public/java/system/src/org/chromium/mojo/system/Flags.java @@ -0,0 +1,83 @@ +// 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. + +package org.chromium.mojo.system; + +/** + * Base class for bit field used as flags. + * + * @param <F> the type of the flags. + */ +public abstract class Flags<F extends Flags<F>> { + private int mFlags; + private boolean mImmutable; + + /** + * Dedicated constructor. + * + * @param flags initial value of the flag. + */ + protected Flags(int flags) { + mImmutable = false; + mFlags = flags; + } + + /** + * @return the computed flag. + */ + public int getFlags() { + return mFlags; + } + + /** + * Change the given bit of this flag. + * + * @param value the new value of given bit. + * @return this. + */ + protected F setFlag(int flag, boolean value) { + if (mImmutable) { + throw new UnsupportedOperationException("Flags is immutable."); + } + if (value) { + mFlags |= flag; + } else { + mFlags &= ~flag; + } + @SuppressWarnings("unchecked") + F f = (F) this; + return f; + } + + /** + * Makes this flag immutable. This is a non-reversable operation. + */ + protected F immutable() { + mImmutable = true; + @SuppressWarnings("unchecked") + F f = (F) this; + return f; + } + + /** + * @see Object#hashCode() + */ + @Override + public int hashCode() { + return mFlags; + } + + /** + * @see Object#equals(Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + Flags<?> other = (Flags<?>) obj; + if (mFlags != other.mFlags) return false; + return true; + } +} diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/Handle.java b/mojo/public/java/system/src/org/chromium/mojo/system/Handle.java new file mode 100644 index 0000000..6181669 --- /dev/null +++ b/mojo/public/java/system/src/org/chromium/mojo/system/Handle.java @@ -0,0 +1,61 @@ +// 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. + +package org.chromium.mojo.system; + +import org.chromium.mojo.system.Core.WaitResult; + +import java.io.Closeable; + +/** + * A generic mojo handle. + */ +public interface Handle extends Closeable { + + /** + * Closes the given |handle|. + * <p> + * Concurrent operations on |handle| may succeed (or fail as usual) if they happen before the + * close, be cancelled with result |MojoResult.CANCELLED| if they properly overlap (this is + * likely the case with |wait()|, etc.), or fail with |MojoResult.INVALID_ARGUMENT| if they + * happen after. + */ + @Override + public void close(); + + /** + * @see Core#wait(Handle, Core.HandleSignals, long) + */ + public WaitResult wait(Core.HandleSignals signals, long deadline); + + /** + * @return whether the handle is valid. A handle is valid until it has been explicitly closed or + * send through a message pipe via |MessagePipeHandle.writeMessage|. + */ + public boolean isValid(); + + /** + * Converts this handle into an {@link UntypedHandle}, invalidating this handle. + */ + public UntypedHandle toUntypedHandle(); + + /** + * Returns the {@link Core} implementation for this handle. Can be null if this handle is + * invalid. + */ + public Core getCore(); + + /** + * Passes ownership of the handle from this handle to the newly created Handle object, + * invalidating this handle object in the process. + */ + public Handle pass(); + + /** + * Releases the native handle backed by this {@link Handle}. The caller owns the handle and must + * close it. + */ + public int releaseNativeHandle(); + +} diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/InvalidHandle.java b/mojo/public/java/system/src/org/chromium/mojo/system/InvalidHandle.java new file mode 100644 index 0000000..9c20fdd --- /dev/null +++ b/mojo/public/java/system/src/org/chromium/mojo/system/InvalidHandle.java @@ -0,0 +1,220 @@ +// 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. + +package org.chromium.mojo.system; + +import org.chromium.mojo.system.Core.HandleSignals; +import org.chromium.mojo.system.Core.WaitResult; +import org.chromium.mojo.system.DataPipe.ConsumerHandle; +import org.chromium.mojo.system.DataPipe.ProducerHandle; + +import java.nio.ByteBuffer; +import java.util.List; + +/** + * A handle that will always be invalid. + */ +public class InvalidHandle implements UntypedHandle, MessagePipeHandle, ConsumerHandle, + ProducerHandle, SharedBufferHandle { + + /** + * Instance singleton. + */ + public static final InvalidHandle INSTANCE = new InvalidHandle(); + + /** + * Private constructor. + */ + private InvalidHandle() { + } + + /** + * @see Handle#close() + */ + @Override + public void close() { + // Do nothing. + } + + /** + * @see Handle#wait(Core.HandleSignals, long) + */ + @Override + public WaitResult wait(HandleSignals signals, long deadline) { + throw new MojoException(MojoResult.INVALID_ARGUMENT); + } + + /** + * @see Handle#isValid() + */ + @Override + public boolean isValid() { + return false; + } + + /** + * @see Handle#getCore() + */ + @Override + public Core getCore() { + return null; + } + + /** + * @see org.chromium.mojo.system.Handle#pass() + */ + @Override + public InvalidHandle pass() { + return this; + } + + /** + * @see Handle#toUntypedHandle() + */ + @Override + public UntypedHandle toUntypedHandle() { + return this; + } + + /** + * @see Handle#releaseNativeHandle() + */ + @Override + public int releaseNativeHandle() { + return 0; + } + + /** + * @see UntypedHandle#toMessagePipeHandle() + */ + @Override + public MessagePipeHandle toMessagePipeHandle() { + return this; + } + + /** + * @see UntypedHandle#toDataPipeConsumerHandle() + */ + @Override + public ConsumerHandle toDataPipeConsumerHandle() { + return this; + } + + /** + * @see UntypedHandle#toDataPipeProducerHandle() + */ + @Override + public ProducerHandle toDataPipeProducerHandle() { + return this; + } + + /** + * @see UntypedHandle#toSharedBufferHandle() + */ + @Override + public SharedBufferHandle toSharedBufferHandle() { + return this; + } + + /** + * @see SharedBufferHandle#duplicate(SharedBufferHandle.DuplicateOptions) + */ + @Override + public SharedBufferHandle duplicate(DuplicateOptions options) { + throw new MojoException(MojoResult.INVALID_ARGUMENT); + } + + /** + * @see SharedBufferHandle#map(long, long, SharedBufferHandle.MapFlags) + */ + @Override + public ByteBuffer map(long offset, long numBytes, MapFlags flags) { + throw new MojoException(MojoResult.INVALID_ARGUMENT); + } + + /** + * @see SharedBufferHandle#unmap(java.nio.ByteBuffer) + */ + @Override + public void unmap(ByteBuffer buffer) { + throw new MojoException(MojoResult.INVALID_ARGUMENT); + } + + /** + * @see DataPipe.ProducerHandle#writeData(java.nio.ByteBuffer, DataPipe.WriteFlags) + */ + @Override + public ResultAnd<Integer> writeData(ByteBuffer elements, DataPipe.WriteFlags flags) { + throw new MojoException(MojoResult.INVALID_ARGUMENT); + } + + /** + * @see DataPipe.ProducerHandle#beginWriteData(int, DataPipe.WriteFlags) + */ + @Override + public ByteBuffer beginWriteData(int numBytes, + DataPipe.WriteFlags flags) { + throw new MojoException(MojoResult.INVALID_ARGUMENT); + } + + /** + * @see DataPipe.ProducerHandle#endWriteData(int) + */ + @Override + public void endWriteData(int numBytesWritten) { + throw new MojoException(MojoResult.INVALID_ARGUMENT); + } + + /** + * @see DataPipe.ConsumerHandle#discardData(int, DataPipe.ReadFlags) + */ + @Override + public int discardData(int numBytes, DataPipe.ReadFlags flags) { + throw new MojoException(MojoResult.INVALID_ARGUMENT); + } + + /** + * @see DataPipe.ConsumerHandle#readData(java.nio.ByteBuffer, DataPipe.ReadFlags) + */ + @Override + public ResultAnd<Integer> readData(ByteBuffer elements, DataPipe.ReadFlags flags) { + throw new MojoException(MojoResult.INVALID_ARGUMENT); + } + + /** + * @see DataPipe.ConsumerHandle#beginReadData(int, DataPipe.ReadFlags) + */ + @Override + public ByteBuffer beginReadData(int numBytes, + DataPipe.ReadFlags flags) { + throw new MojoException(MojoResult.INVALID_ARGUMENT); + } + + /** + * @see DataPipe.ConsumerHandle#endReadData(int) + */ + @Override + public void endReadData(int numBytesRead) { + throw new MojoException(MojoResult.INVALID_ARGUMENT); + } + + /** + * @see MessagePipeHandle#writeMessage(java.nio.ByteBuffer, java.util.List, + * MessagePipeHandle.WriteFlags) + */ + @Override + public void writeMessage(ByteBuffer bytes, List<? extends Handle> handles, WriteFlags flags) { + throw new MojoException(MojoResult.INVALID_ARGUMENT); + } + + /** + * @see MessagePipeHandle#readMessage(java.nio.ByteBuffer, int, MessagePipeHandle.ReadFlags) + */ + @Override + public ResultAnd<ReadMessageResult> readMessage( + ByteBuffer bytes, int maxNumberOfHandles, ReadFlags flags) { + throw new MojoException(MojoResult.INVALID_ARGUMENT); + } + +} diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/MessagePipeHandle.java b/mojo/public/java/system/src/org/chromium/mojo/system/MessagePipeHandle.java new file mode 100644 index 0000000..deb6ac0 --- /dev/null +++ b/mojo/public/java/system/src/org/chromium/mojo/system/MessagePipeHandle.java @@ -0,0 +1,224 @@ +// 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. + +package org.chromium.mojo.system; + +import java.nio.ByteBuffer; +import java.util.List; + +/** + * Message pipes are bidirectional communication channel for framed data (i.e., messages). Messages + * can contain plain data and/or Mojo handles. + */ +public interface MessagePipeHandle extends Handle { + + /** + * Flags for the message pipe creation operation. + */ + public static class CreateFlags extends Flags<CreateFlags> { + private static final int FLAG_NONE = 0; + + /** + * Immutable flag with not bit set. + */ + public static final CreateFlags NONE = CreateFlags.none().immutable(); + + /** + * Dedicated constructor. + * + * @param flags initial value of the flags. + */ + protected CreateFlags(int flags) { + super(flags); + } + + /** + * @return flags with no bit set. + */ + public static CreateFlags none() { + return new CreateFlags(FLAG_NONE); + } + + } + + /** + * Used to specify creation parameters for a message pipe to |Core#createMessagePipe()|. + */ + public static class CreateOptions { + private CreateFlags mFlags = CreateFlags.NONE; + + /** + * @return the flags + */ + public CreateFlags getFlags() { + return mFlags; + } + + } + + /** + * Flags for the write operations on MessagePipeHandle . + */ + public static class WriteFlags extends Flags<WriteFlags> { + private static final int FLAG_NONE = 0; + + /** + * Immutable flag with no bit set. + */ + public static final WriteFlags NONE = WriteFlags.none().immutable(); + + /** + * Dedicated constructor. + * + * @param flags initial value of the flag. + */ + private WriteFlags(int flags) { + super(flags); + } + + /** + * @return a flag with no bit set. + */ + public static WriteFlags none() { + return new WriteFlags(FLAG_NONE); + } + } + + /** + * Flags for the read operations on MessagePipeHandle. + */ + public static class ReadFlags extends Flags<ReadFlags> { + private static final int FLAG_NONE = 0; + private static final int FLAG_MAY_DISCARD = 1 << 0; + + /** + * Immutable flag with no bit set. + */ + public static final ReadFlags NONE = ReadFlags.none().immutable(); + + /** + * Dedicated constructor. + * + * @param flags initial value of the flag. + */ + private ReadFlags(int flags) { + super(flags); + } + + /** + * Change the may-discard bit of this flag. If set, if the message is unable to be read for + * whatever reason (e.g., the caller-supplied buffer is too small), discard the message + * (i.e., simply dequeue it). + * + * @param mayDiscard the new value of the may-discard bit. + * @return this. + */ + public ReadFlags setMayDiscard(boolean mayDiscard) { + return setFlag(FLAG_MAY_DISCARD, mayDiscard); + } + + /** + * @return a flag with no bit set. + */ + public static ReadFlags none() { + return new ReadFlags(FLAG_NONE); + } + + } + + /** + * Result of the |readMessage| method. + */ + public static class ReadMessageResult { + /** + * If a message was read, the size in bytes of the message, otherwise the size in bytes of + * the next message. + */ + private int mMessageSize; + /** + * If a message was read, the number of handles contained in the message, otherwise the + * number of handles contained in the next message. + */ + private int mHandlesCount; + /** + * If a message was read, the handles contained in the message, undefined otherwise. + */ + private List<UntypedHandle> mHandles; + + /** + * @return the messageSize + */ + public int getMessageSize() { + return mMessageSize; + } + + /** + * @param messageSize the messageSize to set + */ + public void setMessageSize(int messageSize) { + mMessageSize = messageSize; + } + + /** + * @return the handlesCount + */ + public int getHandlesCount() { + return mHandlesCount; + } + + /** + * @param handlesCount the handlesCount to set + */ + public void setHandlesCount(int handlesCount) { + mHandlesCount = handlesCount; + } + + /** + * @return the handles + */ + public List<UntypedHandle> getHandles() { + return mHandles; + } + + /** + * @param handles the handles to set + */ + public void setHandles(List<UntypedHandle> handles) { + mHandles = handles; + } + } + + /** + * @see org.chromium.mojo.system.Handle#pass() + */ + @Override + public MessagePipeHandle pass(); + + /** + * Writes a message to the message pipe endpoint, with message data specified by |bytes| and + * attached handles specified by |handles|, and options specified by |flags|. If there is no + * message data, |bytes| may be null, otherwise it must be a direct ByteBuffer. If there are no + * attached handles, |handles| may be null. + * <p> + * If handles are attached, on success the handles will no longer be valid (the receiver will + * receive equivalent, but logically different, handles). Handles to be sent should not be in + * simultaneous use (e.g., on another thread). + */ + void writeMessage(ByteBuffer bytes, List<? extends Handle> handles, WriteFlags flags); + + /** + * Reads a message from the message pipe endpoint; also usable to query the size of the next + * message or discard the next message. |bytes| indicate the buffer/buffer size to receive the + * message data (if any) and |maxNumberOfHandles| indicate the maximum handle count to receive + * the attached handles (if any). |bytes| is optional. If null, |maxNumberOfHandles| must be + * zero, and the return value will indicate the size of the next message. If non-null, it must + * be a direct ByteBuffer and the return value will indicate if the message was read or not. If + * the message was read its content will be in |bytes|, and the attached handles will be in the + * return value. Partial reads are NEVER done. Either a full read is done and |wasMessageRead| + * will be true, or the read is NOT done and |wasMessageRead| will be false (if |mayDiscard| was + * set, the message is also discarded in this case). + */ + ResultAnd<ReadMessageResult> readMessage( + ByteBuffer bytes, int maxNumberOfHandles, ReadFlags flags); +} diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/MojoException.java b/mojo/public/java/system/src/org/chromium/mojo/system/MojoException.java new file mode 100644 index 0000000..4e0e3e9 --- /dev/null +++ b/mojo/public/java/system/src/org/chromium/mojo/system/MojoException.java @@ -0,0 +1,44 @@ +// 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. + +package org.chromium.mojo.system; + +/** + * Exception for the core mojo API. + */ +public class MojoException extends RuntimeException { + + private final int mCode; + + /** + * Constructor. + */ + public MojoException(int code) { + mCode = code; + } + + /** + * Constructor. + */ + public MojoException(Throwable cause) { + super(cause); + mCode = MojoResult.UNKNOWN; + } + + /** + * The mojo result code associated with this exception. See {@link MojoResult} for possible + * values. + */ + public int getMojoResult() { + return mCode; + } + + /** + * @see Object#toString() + */ + @Override + public String toString() { + return "MojoResult(" + mCode + "): " + MojoResult.describe(mCode); + } +} diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/MojoResult.java b/mojo/public/java/system/src/org/chromium/mojo/system/MojoResult.java new file mode 100644 index 0000000..2602cb5 --- /dev/null +++ b/mojo/public/java/system/src/org/chromium/mojo/system/MojoResult.java @@ -0,0 +1,82 @@ +// 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. + +package org.chromium.mojo.system; + +/** + * The different mojo result codes. + */ +public final class MojoResult { + public static final int OK = 0; + public static final int CANCELLED = 1; + public static final int UNKNOWN = 2; + public static final int INVALID_ARGUMENT = 3; + public static final int DEADLINE_EXCEEDED = 4; + public static final int NOT_FOUND = 5; + public static final int ALREADY_EXISTS = 6; + public static final int PERMISSION_DENIED = 7; + public static final int RESOURCE_EXHAUSTED = 8; + public static final int FAILED_PRECONDITION = 9; + public static final int ABORTED = 10; + public static final int OUT_OF_RANGE = 11; + public static final int UNIMPLEMENTED = 12; + public static final int INTERNAL = 13; + public static final int UNAVAILABLE = 14; + public static final int DATA_LOSS = 15; + public static final int BUSY = 16; + public static final int SHOULD_WAIT = 17; + + /** + * never instantiate. + */ + private MojoResult() { + } + + /** + * Describes the given result code. + */ + public static String describe(int mCode) { + switch (mCode) { + case OK: + return "OK"; + case CANCELLED: + return "CANCELLED"; + case UNKNOWN: + return "UNKNOWN"; + case INVALID_ARGUMENT: + return "INVALID_ARGUMENT"; + case DEADLINE_EXCEEDED: + return "DEADLINE_EXCEEDED"; + case NOT_FOUND: + return "NOT_FOUND"; + case ALREADY_EXISTS: + return "ALREADY_EXISTS"; + case PERMISSION_DENIED: + return "PERMISSION_DENIED"; + case RESOURCE_EXHAUSTED: + return "RESOURCE_EXHAUSTED"; + case FAILED_PRECONDITION: + return "FAILED_PRECONDITION"; + case ABORTED: + return "ABORTED"; + case OUT_OF_RANGE: + return "OUT_OF_RANGE"; + case UNIMPLEMENTED: + return "UNIMPLEMENTED"; + case INTERNAL: + return "INTERNAL"; + case UNAVAILABLE: + return "UNAVAILABLE"; + case DATA_LOSS: + return "DATA_LOSS"; + case BUSY: + return "BUSY"; + case SHOULD_WAIT: + return "SHOULD_WAIT"; + default: + return "UNKNOWN"; + } + + } +} diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/Pair.java b/mojo/public/java/system/src/org/chromium/mojo/system/Pair.java new file mode 100644 index 0000000..2ead020 --- /dev/null +++ b/mojo/public/java/system/src/org/chromium/mojo/system/Pair.java @@ -0,0 +1,67 @@ +// 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. + +package org.chromium.mojo.system; + + +/** + * A pair of object. + * + * @param <F> Type of the first element. + * @param <S> Type of the second element. + */ +public class Pair<F, S> { + + public final F first; + public final S second; + + /** + * Dedicated constructor. + * + * @param first the first element of the pair. + * @param second the second element of the pair. + */ + public Pair(F first, S second) { + this.first = first; + this.second = second; + } + + /** + * equals() that handles null values. + */ + private boolean equals(Object o1, Object o2) { + return o1 == null ? o2 == null : o1.equals(o2); + } + + /** + * @see Object#equals(Object) + */ + @Override + public boolean equals(Object o) { + if (!(o instanceof Pair)) { + return false; + } + Pair<?, ?> p = (Pair<?, ?>) o; + return equals(first, p.first) && equals(second, p.second); + } + + /** + * @see Object#hashCode() + */ + @Override + public int hashCode() { + return (first == null ? 0 : first.hashCode()) ^ (second == null ? 0 : second.hashCode()); + } + + /** + * Helper method for creating a pair. + * + * @param a the first element of the pair. + * @param b the second element of the pair. + * @return the pair containing a and b. + */ + public static <A, B> Pair<A, B> create(A a, B b) { + return new Pair<A, B>(a, b); + } +} diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/ResultAnd.java b/mojo/public/java/system/src/org/chromium/mojo/system/ResultAnd.java new file mode 100644 index 0000000..656d0d6 --- /dev/null +++ b/mojo/public/java/system/src/org/chromium/mojo/system/ResultAnd.java @@ -0,0 +1,34 @@ +// 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. + +package org.chromium.mojo.system; + +/** + * Container that contains a mojo result and a value. + * + * @param <A> the type of the value. + */ +public class ResultAnd<A> { + private final int mMojoResult; + private final A mValue; + + public ResultAnd(int result, A value) { + this.mMojoResult = result; + this.mValue = value; + } + + /** + * Returns the mojo result. + */ + public int getMojoResult() { + return mMojoResult; + } + + /** + * Returns the value. + */ + public A getValue() { + return mValue; + } +} diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/RunLoop.java b/mojo/public/java/system/src/org/chromium/mojo/system/RunLoop.java new file mode 100644 index 0000000..4038b295 --- /dev/null +++ b/mojo/public/java/system/src/org/chromium/mojo/system/RunLoop.java @@ -0,0 +1,41 @@ +// 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. + +package org.chromium.mojo.system; + +import java.io.Closeable; + +/** + * Definition of a run loop. + */ +public interface RunLoop extends Closeable { + /** + * Start the run loop. It will continue until quit() is called. + */ + public void run(); + + /** + * Start the run loop and stop it as soon as no task is present in the work queue. + */ + public void runUntilIdle(); + + /* + * Quit the currently running run loop. + */ + public void quit(); + + /** + * Add a runnable to the queue of tasks. + * @param runnable Callback to be executed by the run loop. + * @param delay Delay, in MojoTimeTicks (microseconds) before the callback should + * be executed. + */ + public void postDelayedTask(Runnable runnable, long delay); + + /** + * Destroy the run loop and deregister it from Core. + */ + @Override + public abstract void close(); +} diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/SharedBufferHandle.java b/mojo/public/java/system/src/org/chromium/mojo/system/SharedBufferHandle.java new file mode 100644 index 0000000..df317d1 --- /dev/null +++ b/mojo/public/java/system/src/org/chromium/mojo/system/SharedBufferHandle.java @@ -0,0 +1,160 @@ +// 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. + +package org.chromium.mojo.system; + +import java.nio.ByteBuffer; + +/** + * A buffer that can be shared between applications. + */ +public interface SharedBufferHandle extends Handle { + + /** + * Flags for the shared buffer creation operation. + */ + public static class CreateFlags extends Flags<CreateFlags> { + private static final int FLAG_NONE = 0; + + /** + * Immutable flag with not bit set. + */ + public static final CreateFlags NONE = CreateFlags.none().immutable(); + + /** + * Dedicated constructor. + * + * @param flags initial value of the flags. + */ + protected CreateFlags(int flags) { + super(flags); + } + + /** + * @return flags with no bit set. + */ + public static CreateFlags none() { + return new CreateFlags(FLAG_NONE); + } + + } + + /** + * Used to specify creation parameters for a shared buffer to |Core#createSharedBuffer()|. + */ + public static class CreateOptions { + private CreateFlags mFlags = CreateFlags.NONE; + + /** + * @return the flags + */ + public CreateFlags getFlags() { + return mFlags; + } + + } + + /** + * Flags for the shared buffer duplication operation. + */ + public static class DuplicateFlags extends Flags<DuplicateFlags> { + private static final int FLAG_NONE = 0; + + /** + * Immutable flag with not bit set. + */ + public static final DuplicateFlags NONE = DuplicateFlags.none().immutable(); + + /** + * Dedicated constructor. + * + * @param flags initial value of the flags. + */ + protected DuplicateFlags(int flags) { + super(flags); + } + + /** + * @return flags with no bit set. + */ + public static DuplicateFlags none() { + return new DuplicateFlags(FLAG_NONE); + } + + } + + /** + * Used to specify parameters in duplicating access to a shared buffer to + * |SharedBufferHandle#duplicate| + */ + public static class DuplicateOptions { + private DuplicateFlags mFlags = DuplicateFlags.NONE; + + /** + * @return the flags + */ + public DuplicateFlags getFlags() { + return mFlags; + } + + } + + /** + * Flags for the shared buffer map operation. + */ + public static class MapFlags extends Flags<MapFlags> { + private static final int FLAG_NONE = 0; + + /** + * Immutable flag with not bit set. + */ + public static final MapFlags NONE = MapFlags.none().immutable(); + + /** + * Dedicated constructor. + * + * @param flags initial value of the flags. + */ + protected MapFlags(int flags) { + super(flags); + } + + /** + * @return flags with no bit set. + */ + public static MapFlags none() { + return new MapFlags(FLAG_NONE); + } + + } + + /** + * @see org.chromium.mojo.system.Handle#pass() + */ + @Override + public SharedBufferHandle pass(); + + /** + * Duplicates the handle. This creates another handle (returned on success), which can then be + * sent to another application over a message pipe, while retaining access to this handle (and + * any mappings that it may have). + */ + public SharedBufferHandle duplicate(DuplicateOptions options); + + /** + * Map the part (at offset |offset| of length |numBytes|) of the buffer given by this handle + * into memory. |offset + numBytes| must be less than or equal to the size of the buffer. On + * success, the returned buffer points to memory with the requested part of the buffer. A single + * buffer handle may have multiple active mappings (possibly depending on the buffer type). The + * permissions (e.g., writable or executable) of the returned memory may depend on the + * properties of the buffer and properties attached to the buffer handle as well as |flags|. + */ + public ByteBuffer map(long offset, long numBytes, MapFlags flags); + + /** + * Unmap a buffer pointer that was mapped by |map()|. + */ + public void unmap(ByteBuffer buffer); + +} diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/UntypedHandle.java b/mojo/public/java/system/src/org/chromium/mojo/system/UntypedHandle.java new file mode 100644 index 0000000..199b0a1 --- /dev/null +++ b/mojo/public/java/system/src/org/chromium/mojo/system/UntypedHandle.java @@ -0,0 +1,45 @@ +// 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. + +package org.chromium.mojo.system; + +import org.chromium.mojo.system.DataPipe.ConsumerHandle; +import org.chromium.mojo.system.DataPipe.ProducerHandle; + +/** + * A mojo handle of unknown type. This handle can be typed by using one of its methods, which will + * return a handle of the requested type and invalidate this object. No validation is made when the + * conversion operation is called. + */ +public interface UntypedHandle extends Handle { + + /** + * @see org.chromium.mojo.system.Handle#pass() + */ + @Override + public UntypedHandle pass(); + + /** + * Returns the underlying handle, as a {@link MessagePipeHandle}, invalidating this + * representation. + */ + public MessagePipeHandle toMessagePipeHandle(); + + /** + * Returns the underlying handle, as a {@link ConsumerHandle}, invalidating this representation. + */ + public ConsumerHandle toDataPipeConsumerHandle(); + + /** + * Returns the underlying handle, as a {@link ProducerHandle}, invalidating this representation. + */ + public ProducerHandle toDataPipeProducerHandle(); + + /** + * Returns the underlying handle, as a {@link SharedBufferHandle}, invalidating this + * representation. + */ + public SharedBufferHandle toSharedBufferHandle(); + +} diff --git a/mojo/public/js/BUILD.gn b/mojo/public/js/BUILD.gn new file mode 100644 index 0000000..1cb1a60 --- /dev/null +++ b/mojo/public/js/BUILD.gn @@ -0,0 +1,12 @@ +# 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. + +import("../mojo_sdk.gni") + +mojo_sdk_source_set("js") { + sources = [ + "constants.cc", + "constants.h", + ] +} diff --git a/mojo/public/js/bindings.js b/mojo/public/js/bindings.js new file mode 100644 index 0000000..44aa9f4 --- /dev/null +++ b/mojo/public/js/bindings.js @@ -0,0 +1,128 @@ +// 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. + +define("mojo/public/js/bindings", [ + "mojo/public/js/router", + "mojo/public/js/core", +], function(router, core) { + + var Router = router.Router; + + var kProxyProperties = Symbol("proxyProperties"); + var kStubProperties = Symbol("stubProperties"); + + // Public proxy class properties that are managed at runtime by the JS + // bindings. See ProxyBindings below. + function ProxyProperties(receiver) { + this.receiver = receiver; + } + + // TODO(hansmuller): remove then after 'Client=' has been removed from Mojom. + ProxyProperties.prototype.getLocalDelegate = function() { + return this.local && StubBindings(this.local).delegate; + } + + // TODO(hansmuller): remove then after 'Client=' has been removed from Mojom. + ProxyProperties.prototype.setLocalDelegate = function(impl) { + if (this.local) + StubBindings(this.local).delegate = impl; + else + throw new Error("no stub object"); + } + + function connectionHandle(connection) { + return connection && + connection.router && + connection.router.connector_ && + connection.router.connector_.handle_; + } + + ProxyProperties.prototype.close = function() { + var handle = connectionHandle(this.connection); + if (handle) + core.close(handle); + } + + // Public stub class properties that are managed at runtime by the JS + // bindings. See StubBindings below. + function StubProperties(delegate) { + this.delegate = delegate; + } + + StubProperties.prototype.close = function() { + var handle = connectionHandle(this.connection); + if (handle) + core.close(handle); + } + + // The base class for generated proxy classes. + function ProxyBase(receiver) { + this[kProxyProperties] = new ProxyProperties(receiver); + + // TODO(hansmuller): Temporary, for Chrome backwards compatibility. + if (receiver instanceof Router) + this.receiver_ = receiver; + } + + // The base class for generated stub classes. + function StubBase(delegate) { + this[kStubProperties] = new StubProperties(delegate); + } + + // TODO(hansmuller): remove everything except the connection property doc + // after 'Client=' has been removed from Mojom. + + // Provides access to properties added to a proxy object without risking + // Mojo interface name collisions. Unless otherwise specified, the initial + // value of all properties is undefined. + // + // ProxyBindings(proxy).connection - The Connection object that links the + // proxy for a remote Mojo service to an optional local stub for a local + // service. The value of ProxyBindings(proxy).connection.remote == proxy. + // + // ProxyBindings(proxy).local - The "local" stub object whose delegate + // implements the proxy's Mojo client interface. + // + // ProxyBindings(proxy).setLocalDelegate(impl) - Sets the implementation + // delegate of the proxy's client stub object. This is just shorthand + // for |StubBindings(ProxyBindings(proxy).local).delegate = impl|. + // + // ProxyBindings(proxy).getLocalDelegate() - Returns the implementation + // delegate of the proxy's client stub object. This is just shorthand + // for |StubBindings(ProxyBindings(proxy).local).delegate|. + + function ProxyBindings(proxy) { + return (proxy instanceof ProxyBase) ? proxy[kProxyProperties] : proxy; + } + + // TODO(hansmuller): remove the remote doc after 'Client=' has been + // removed from Mojom. + + // Provides access to properties added to a stub object without risking + // Mojo interface name collisions. Unless otherwise specified, the initial + // value of all properties is undefined. + // + // StubBindings(stub).delegate - The optional implementation delegate for + // the Mojo interface stub. + // + // StubBindings(stub).connection - The Connection object that links an + // optional proxy for a remote service to this stub. The value of + // StubBindings(stub).connection.local == stub. + // + // StubBindings(stub).remote - A proxy for the the stub's Mojo client + // service. + + function StubBindings(stub) { + return stub instanceof StubBase ? stub[kStubProperties] : stub; + } + + var exports = {}; + exports.EmptyProxy = ProxyBase; + exports.EmptyStub = StubBase; + exports.ProxyBase = ProxyBase; + exports.ProxyBindings = ProxyBindings; + exports.StubBase = StubBase; + exports.StubBindings = StubBindings; + return exports; +});
\ No newline at end of file diff --git a/mojo/public/js/buffer.js b/mojo/public/js/buffer.js new file mode 100644 index 0000000..e35f695 --- /dev/null +++ b/mojo/public/js/buffer.js @@ -0,0 +1,156 @@ +// 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. + +define("mojo/public/js/buffer", function() { + + var kHostIsLittleEndian = (function () { + var endianArrayBuffer = new ArrayBuffer(2); + var endianUint8Array = new Uint8Array(endianArrayBuffer); + var endianUint16Array = new Uint16Array(endianArrayBuffer); + endianUint16Array[0] = 1; + return endianUint8Array[0] == 1; + })(); + + var kHighWordMultiplier = 0x100000000; + + function Buffer(sizeOrArrayBuffer) { + if (sizeOrArrayBuffer instanceof ArrayBuffer) + this.arrayBuffer = sizeOrArrayBuffer; + else + this.arrayBuffer = new ArrayBuffer(sizeOrArrayBuffer); + + this.dataView = new DataView(this.arrayBuffer); + this.next = 0; + } + + Object.defineProperty(Buffer.prototype, "byteLength", { + get: function() { return this.arrayBuffer.byteLength; } + }); + + Buffer.prototype.alloc = function(size) { + var pointer = this.next; + this.next += size; + if (this.next > this.byteLength) { + var newSize = (1.5 * (this.byteLength + size)) | 0; + this.grow(newSize); + } + return pointer; + }; + + function copyArrayBuffer(dstArrayBuffer, srcArrayBuffer) { + (new Uint8Array(dstArrayBuffer)).set(new Uint8Array(srcArrayBuffer)); + } + + Buffer.prototype.grow = function(size) { + var newArrayBuffer = new ArrayBuffer(size); + copyArrayBuffer(newArrayBuffer, this.arrayBuffer); + this.arrayBuffer = newArrayBuffer; + this.dataView = new DataView(this.arrayBuffer); + }; + + Buffer.prototype.trim = function() { + this.arrayBuffer = this.arrayBuffer.slice(0, this.next); + this.dataView = new DataView(this.arrayBuffer); + }; + + Buffer.prototype.getUint8 = function(offset) { + return this.dataView.getUint8(offset); + } + Buffer.prototype.getUint16 = function(offset) { + return this.dataView.getUint16(offset, kHostIsLittleEndian); + } + Buffer.prototype.getUint32 = function(offset) { + return this.dataView.getUint32(offset, kHostIsLittleEndian); + } + Buffer.prototype.getUint64 = function(offset) { + var lo, hi; + if (kHostIsLittleEndian) { + lo = this.dataView.getUint32(offset, kHostIsLittleEndian); + hi = this.dataView.getUint32(offset + 4, kHostIsLittleEndian); + } else { + hi = this.dataView.getUint32(offset, kHostIsLittleEndian); + lo = this.dataView.getUint32(offset + 4, kHostIsLittleEndian); + } + return lo + hi * kHighWordMultiplier; + } + + Buffer.prototype.getInt8 = function(offset) { + return this.dataView.getInt8(offset); + } + Buffer.prototype.getInt16 = function(offset) { + return this.dataView.getInt16(offset, kHostIsLittleEndian); + } + Buffer.prototype.getInt32 = function(offset) { + return this.dataView.getInt32(offset, kHostIsLittleEndian); + } + Buffer.prototype.getInt64 = function(offset) { + var lo, hi; + if (kHostIsLittleEndian) { + lo = this.dataView.getUint32(offset, kHostIsLittleEndian); + hi = this.dataView.getInt32(offset + 4, kHostIsLittleEndian); + } else { + hi = this.dataView.getInt32(offset, kHostIsLittleEndian); + lo = this.dataView.getUint32(offset + 4, kHostIsLittleEndian); + } + return lo + hi * kHighWordMultiplier; + } + + Buffer.prototype.getFloat32 = function(offset) { + return this.dataView.getFloat32(offset, kHostIsLittleEndian); + } + Buffer.prototype.getFloat64 = function(offset) { + return this.dataView.getFloat64(offset, kHostIsLittleEndian); + } + + Buffer.prototype.setUint8 = function(offset, value) { + this.dataView.setUint8(offset, value); + } + Buffer.prototype.setUint16 = function(offset, value) { + this.dataView.setUint16(offset, value, kHostIsLittleEndian); + } + Buffer.prototype.setUint32 = function(offset, value) { + this.dataView.setUint32(offset, value, kHostIsLittleEndian); + } + Buffer.prototype.setUint64 = function(offset, value) { + var hi = (value / kHighWordMultiplier) | 0; + if (kHostIsLittleEndian) { + this.dataView.setInt32(offset, value, kHostIsLittleEndian); + this.dataView.setInt32(offset + 4, hi, kHostIsLittleEndian); + } else { + this.dataView.setInt32(offset, hi, kHostIsLittleEndian); + this.dataView.setInt32(offset + 4, value, kHostIsLittleEndian); + } + } + + Buffer.prototype.setInt8 = function(offset, value) { + this.dataView.setInt8(offset, value); + } + Buffer.prototype.setInt16 = function(offset, value) { + this.dataView.setInt16(offset, value, kHostIsLittleEndian); + } + Buffer.prototype.setInt32 = function(offset, value) { + this.dataView.setInt32(offset, value, kHostIsLittleEndian); + } + Buffer.prototype.setInt64 = function(offset, value) { + var hi = Math.floor(value / kHighWordMultiplier); + if (kHostIsLittleEndian) { + this.dataView.setInt32(offset, value, kHostIsLittleEndian); + this.dataView.setInt32(offset + 4, hi, kHostIsLittleEndian); + } else { + this.dataView.setInt32(offset, hi, kHostIsLittleEndian); + this.dataView.setInt32(offset + 4, value, kHostIsLittleEndian); + } + } + + Buffer.prototype.setFloat32 = function(offset, value) { + this.dataView.setFloat32(offset, value, kHostIsLittleEndian); + } + Buffer.prototype.setFloat64 = function(offset, value) { + this.dataView.setFloat64(offset, value, kHostIsLittleEndian); + } + + var exports = {}; + exports.Buffer = Buffer; + return exports; +}); diff --git a/mojo/public/js/codec.js b/mojo/public/js/codec.js new file mode 100644 index 0000000..4003b51 --- /dev/null +++ b/mojo/public/js/codec.js @@ -0,0 +1,879 @@ +// 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. + +define("mojo/public/js/codec", [ + "mojo/public/js/unicode", + "mojo/public/js/buffer", +], function(unicode, buffer) { + + var kErrorUnsigned = "Passing negative value to unsigned"; + var kErrorArray = "Passing non Array for array type"; + var kErrorString = "Passing non String for string type"; + var kErrorMap = "Passing non Map for map type"; + + // Memory ------------------------------------------------------------------- + + var kAlignment = 8; + + function align(size) { + return size + (kAlignment - (size % kAlignment)) % kAlignment; + } + + function isAligned(offset) { + return offset >= 0 && (offset % kAlignment) === 0; + } + + // Constants ---------------------------------------------------------------- + + var kArrayHeaderSize = 8; + var kStructHeaderSize = 8; + var kMessageHeaderSize = 24; + var kMessageWithRequestIDHeaderSize = 32; + var kMapStructPayloadSize = 16; + + var kStructHeaderNumBytesOffset = 0; + var kStructHeaderVersionOffset = 4; + + var kEncodedInvalidHandleValue = 0xFFFFFFFF; + + // Decoder ------------------------------------------------------------------ + + function Decoder(buffer, handles, base) { + this.buffer = buffer; + this.handles = handles; + this.base = base; + this.next = base; + } + + Decoder.prototype.align = function() { + this.next = align(this.next); + }; + + Decoder.prototype.skip = function(offset) { + this.next += offset; + }; + + Decoder.prototype.readInt8 = function() { + var result = this.buffer.getInt8(this.next); + this.next += 1; + return result; + }; + + Decoder.prototype.readUint8 = function() { + var result = this.buffer.getUint8(this.next); + this.next += 1; + return result; + }; + + Decoder.prototype.readInt16 = function() { + var result = this.buffer.getInt16(this.next); + this.next += 2; + return result; + }; + + Decoder.prototype.readUint16 = function() { + var result = this.buffer.getUint16(this.next); + this.next += 2; + return result; + }; + + Decoder.prototype.readInt32 = function() { + var result = this.buffer.getInt32(this.next); + this.next += 4; + return result; + }; + + Decoder.prototype.readUint32 = function() { + var result = this.buffer.getUint32(this.next); + this.next += 4; + return result; + }; + + Decoder.prototype.readInt64 = function() { + var result = this.buffer.getInt64(this.next); + this.next += 8; + return result; + }; + + Decoder.prototype.readUint64 = function() { + var result = this.buffer.getUint64(this.next); + this.next += 8; + return result; + }; + + Decoder.prototype.readFloat = function() { + var result = this.buffer.getFloat32(this.next); + this.next += 4; + return result; + }; + + Decoder.prototype.readDouble = function() { + var result = this.buffer.getFloat64(this.next); + this.next += 8; + return result; + }; + + Decoder.prototype.decodePointer = function() { + // TODO(abarth): To correctly decode a pointer, we need to know the real + // base address of the array buffer. + var offsetPointer = this.next; + var offset = this.readUint64(); + if (!offset) + return 0; + return offsetPointer + offset; + }; + + Decoder.prototype.decodeAndCreateDecoder = function(pointer) { + return new Decoder(this.buffer, this.handles, pointer); + }; + + Decoder.prototype.decodeHandle = function() { + return this.handles[this.readUint32()] || null; + }; + + Decoder.prototype.decodeString = function() { + var numberOfBytes = this.readUint32(); + var numberOfElements = this.readUint32(); + var base = this.next; + this.next += numberOfElements; + return unicode.decodeUtf8String( + new Uint8Array(this.buffer.arrayBuffer, base, numberOfElements)); + }; + + Decoder.prototype.decodeArray = function(cls) { + var numberOfBytes = this.readUint32(); + var numberOfElements = this.readUint32(); + var val = new Array(numberOfElements); + if (cls === PackedBool) { + var byte; + for (var i = 0; i < numberOfElements; ++i) { + if (i % 8 === 0) + byte = this.readUint8(); + val[i] = (byte & (1 << i % 8)) ? true : false; + } + } else { + for (var i = 0; i < numberOfElements; ++i) { + val[i] = cls.decode(this); + } + } + return val; + }; + + Decoder.prototype.decodeStruct = function(cls) { + return cls.decode(this); + }; + + Decoder.prototype.decodeStructPointer = function(cls) { + var pointer = this.decodePointer(); + if (!pointer) { + return null; + } + return cls.decode(this.decodeAndCreateDecoder(pointer)); + }; + + Decoder.prototype.decodeArrayPointer = function(cls) { + var pointer = this.decodePointer(); + if (!pointer) { + return null; + } + return this.decodeAndCreateDecoder(pointer).decodeArray(cls); + }; + + Decoder.prototype.decodeStringPointer = function() { + var pointer = this.decodePointer(); + if (!pointer) { + return null; + } + return this.decodeAndCreateDecoder(pointer).decodeString(); + }; + + Decoder.prototype.decodeMap = function(keyClass, valueClass) { + this.skip(4); // numberOfBytes + this.skip(4); // version + var keys = this.decodeArrayPointer(keyClass); + var values = this.decodeArrayPointer(valueClass); + var val = new Map(); + for (var i = 0; i < keys.length; i++) + val.set(keys[i], values[i]); + return val; + }; + + Decoder.prototype.decodeMapPointer = function(keyClass, valueClass) { + var pointer = this.decodePointer(); + if (!pointer) { + return null; + } + var decoder = this.decodeAndCreateDecoder(pointer); + return decoder.decodeMap(keyClass, valueClass); + }; + + // Encoder ------------------------------------------------------------------ + + function Encoder(buffer, handles, base) { + this.buffer = buffer; + this.handles = handles; + this.base = base; + this.next = base; + } + + Encoder.prototype.align = function() { + this.next = align(this.next); + }; + + Encoder.prototype.skip = function(offset) { + this.next += offset; + }; + + Encoder.prototype.writeInt8 = function(val) { + this.buffer.setInt8(this.next, val); + this.next += 1; + }; + + Encoder.prototype.writeUint8 = function(val) { + if (val < 0) { + throw new Error(kErrorUnsigned); + } + this.buffer.setUint8(this.next, val); + this.next += 1; + }; + + Encoder.prototype.writeInt16 = function(val) { + this.buffer.setInt16(this.next, val); + this.next += 2; + }; + + Encoder.prototype.writeUint16 = function(val) { + if (val < 0) { + throw new Error(kErrorUnsigned); + } + this.buffer.setUint16(this.next, val); + this.next += 2; + }; + + Encoder.prototype.writeInt32 = function(val) { + this.buffer.setInt32(this.next, val); + this.next += 4; + }; + + Encoder.prototype.writeUint32 = function(val) { + if (val < 0) { + throw new Error(kErrorUnsigned); + } + this.buffer.setUint32(this.next, val); + this.next += 4; + }; + + Encoder.prototype.writeInt64 = function(val) { + this.buffer.setInt64(this.next, val); + this.next += 8; + }; + + Encoder.prototype.writeUint64 = function(val) { + if (val < 0) { + throw new Error(kErrorUnsigned); + } + this.buffer.setUint64(this.next, val); + this.next += 8; + }; + + Encoder.prototype.writeFloat = function(val) { + this.buffer.setFloat32(this.next, val); + this.next += 4; + }; + + Encoder.prototype.writeDouble = function(val) { + this.buffer.setFloat64(this.next, val); + this.next += 8; + }; + + Encoder.prototype.encodePointer = function(pointer) { + if (!pointer) + return this.writeUint64(0); + // TODO(abarth): To correctly encode a pointer, we need to know the real + // base address of the array buffer. + var offset = pointer - this.next; + this.writeUint64(offset); + }; + + Encoder.prototype.createAndEncodeEncoder = function(size) { + var pointer = this.buffer.alloc(align(size)); + this.encodePointer(pointer); + return new Encoder(this.buffer, this.handles, pointer); + }; + + Encoder.prototype.encodeHandle = function(handle) { + this.handles.push(handle); + this.writeUint32(this.handles.length - 1); + }; + + Encoder.prototype.encodeString = function(val) { + var base = this.next + kArrayHeaderSize; + var numberOfElements = unicode.encodeUtf8String( + val, new Uint8Array(this.buffer.arrayBuffer, base)); + var numberOfBytes = kArrayHeaderSize + numberOfElements; + this.writeUint32(numberOfBytes); + this.writeUint32(numberOfElements); + this.next += numberOfElements; + }; + + Encoder.prototype.encodeArray = + function(cls, val, numberOfElements, encodedSize) { + if (numberOfElements === undefined) + numberOfElements = val.length; + if (encodedSize === undefined) + encodedSize = kArrayHeaderSize + cls.encodedSize * numberOfElements; + + this.writeUint32(encodedSize); + this.writeUint32(numberOfElements); + + if (cls === PackedBool) { + var byte = 0; + for (i = 0; i < numberOfElements; ++i) { + if (val[i]) + byte |= (1 << i % 8); + if (i % 8 === 7 || i == numberOfElements - 1) { + Uint8.encode(this, byte); + byte = 0; + } + } + } else { + for (var i = 0; i < numberOfElements; ++i) + cls.encode(this, val[i]); + } + }; + + Encoder.prototype.encodeStruct = function(cls, val) { + return cls.encode(this, val); + }; + + Encoder.prototype.encodeStructPointer = function(cls, val) { + if (val == null) { + // Also handles undefined, since undefined == null. + this.encodePointer(val); + return; + } + var encoder = this.createAndEncodeEncoder(cls.encodedSize); + cls.encode(encoder, val); + }; + + Encoder.prototype.encodeArrayPointer = function(cls, val) { + if (val == null) { + // Also handles undefined, since undefined == null. + this.encodePointer(val); + return; + } + + var numberOfElements = val.length; + if (!Number.isSafeInteger(numberOfElements) || numberOfElements < 0) + throw new Error(kErrorArray); + + var encodedSize = kArrayHeaderSize + ((cls === PackedBool) ? + Math.ceil(numberOfElements / 8) : cls.encodedSize * numberOfElements); + var encoder = this.createAndEncodeEncoder(encodedSize); + encoder.encodeArray(cls, val, numberOfElements, encodedSize); + }; + + Encoder.prototype.encodeStringPointer = function(val) { + if (val == null) { + // Also handles undefined, since undefined == null. + this.encodePointer(val); + return; + } + // Only accepts string primivites, not String Objects like new String("foo") + if (typeof(val) !== "string") { + throw new Error(kErrorString); + } + var encodedSize = kArrayHeaderSize + unicode.utf8Length(val); + var encoder = this.createAndEncodeEncoder(encodedSize); + encoder.encodeString(val); + }; + + Encoder.prototype.encodeMap = function(keyClass, valueClass, val) { + var keys = new Array(val.size); + var values = new Array(val.size); + var i = 0; + val.forEach(function(value, key) { + values[i] = value; + keys[i++] = key; + }); + this.writeUint32(kStructHeaderSize + kMapStructPayloadSize); + this.writeUint32(0); // version + this.encodeArrayPointer(keyClass, keys); + this.encodeArrayPointer(valueClass, values); + } + + Encoder.prototype.encodeMapPointer = function(keyClass, valueClass, val) { + if (val == null) { + // Also handles undefined, since undefined == null. + this.encodePointer(val); + return; + } + if (!(val instanceof Map)) { + throw new Error(kErrorMap); + } + var encodedSize = kStructHeaderSize + kMapStructPayloadSize; + var encoder = this.createAndEncodeEncoder(encodedSize); + encoder.encodeMap(keyClass, valueClass, val); + }; + + // Message ------------------------------------------------------------------ + + var kMessageInterfaceIdOffset = kStructHeaderSize; + var kMessageNameOffset = kMessageInterfaceIdOffset + 4; + var kMessageFlagsOffset = kMessageNameOffset + 4; + var kMessageRequestIDOffset = kMessageFlagsOffset + 8; + + var kMessageExpectsResponse = 1 << 0; + var kMessageIsResponse = 1 << 1; + + function Message(buffer, handles) { + this.buffer = buffer; + this.handles = handles; + } + + Message.prototype.getHeaderNumBytes = function() { + return this.buffer.getUint32(kStructHeaderNumBytesOffset); + }; + + Message.prototype.getHeaderVersion = function() { + return this.buffer.getUint32(kStructHeaderVersionOffset); + }; + + Message.prototype.getName = function() { + return this.buffer.getUint32(kMessageNameOffset); + }; + + Message.prototype.getFlags = function() { + return this.buffer.getUint32(kMessageFlagsOffset); + }; + + Message.prototype.isResponse = function() { + return (this.getFlags() & kMessageIsResponse) != 0; + }; + + Message.prototype.expectsResponse = function() { + return (this.getFlags() & kMessageExpectsResponse) != 0; + }; + + Message.prototype.setRequestID = function(requestID) { + // TODO(darin): Verify that space was reserved for this field! + this.buffer.setUint64(kMessageRequestIDOffset, requestID); + }; + + + // MessageBuilder ----------------------------------------------------------- + + function MessageBuilder(messageName, payloadSize) { + // Currently, we don't compute the payload size correctly ahead of time. + // Instead, we resize the buffer at the end. + var numberOfBytes = kMessageHeaderSize + payloadSize; + this.buffer = new buffer.Buffer(numberOfBytes); + this.handles = []; + var encoder = this.createEncoder(kMessageHeaderSize); + encoder.writeUint32(kMessageHeaderSize); + encoder.writeUint32(0); // version. + encoder.writeUint32(0); // interface ID. + encoder.writeUint32(messageName); + encoder.writeUint32(0); // flags. + encoder.writeUint32(0); // padding. + } + + MessageBuilder.prototype.createEncoder = function(size) { + var pointer = this.buffer.alloc(size); + return new Encoder(this.buffer, this.handles, pointer); + }; + + MessageBuilder.prototype.encodeStruct = function(cls, val) { + cls.encode(this.createEncoder(cls.encodedSize), val); + }; + + MessageBuilder.prototype.finish = function() { + // TODO(abarth): Rather than resizing the buffer at the end, we could + // compute the size we need ahead of time, like we do in C++. + this.buffer.trim(); + var message = new Message(this.buffer, this.handles); + this.buffer = null; + this.handles = null; + this.encoder = null; + return message; + }; + + // MessageWithRequestIDBuilder ----------------------------------------------- + + function MessageWithRequestIDBuilder(messageName, payloadSize, flags, + requestID) { + // Currently, we don't compute the payload size correctly ahead of time. + // Instead, we resize the buffer at the end. + var numberOfBytes = kMessageWithRequestIDHeaderSize + payloadSize; + this.buffer = new buffer.Buffer(numberOfBytes); + this.handles = []; + var encoder = this.createEncoder(kMessageWithRequestIDHeaderSize); + encoder.writeUint32(kMessageWithRequestIDHeaderSize); + encoder.writeUint32(1); // version. + encoder.writeUint32(0); // interface ID. + encoder.writeUint32(messageName); + encoder.writeUint32(flags); + encoder.writeUint32(0); // padding. + encoder.writeUint64(requestID); + } + + MessageWithRequestIDBuilder.prototype = + Object.create(MessageBuilder.prototype); + + MessageWithRequestIDBuilder.prototype.constructor = + MessageWithRequestIDBuilder; + + // MessageReader ------------------------------------------------------------ + + function MessageReader(message) { + this.decoder = new Decoder(message.buffer, message.handles, 0); + var messageHeaderSize = this.decoder.readUint32(); + this.payloadSize = message.buffer.byteLength - messageHeaderSize; + var version = this.decoder.readUint32(); + var interface_id = this.decoder.readUint32(); + if (interface_id != 0) { + throw new Error("Receiving non-zero interface ID. Associated interfaces " + + "are not yet supported."); + } + this.messageName = this.decoder.readUint32(); + this.flags = this.decoder.readUint32(); + // Skip the padding. + this.decoder.skip(4); + if (version >= 1) + this.requestID = this.decoder.readUint64(); + this.decoder.skip(messageHeaderSize - this.decoder.next); + } + + MessageReader.prototype.decodeStruct = function(cls) { + return cls.decode(this.decoder); + }; + + // Built-in types ----------------------------------------------------------- + + // This type is only used with ArrayOf(PackedBool). + function PackedBool() { + } + + function Int8() { + } + + Int8.encodedSize = 1; + + Int8.decode = function(decoder) { + return decoder.readInt8(); + }; + + Int8.encode = function(encoder, val) { + encoder.writeInt8(val); + }; + + Uint8.encode = function(encoder, val) { + encoder.writeUint8(val); + }; + + function Uint8() { + } + + Uint8.encodedSize = 1; + + Uint8.decode = function(decoder) { + return decoder.readUint8(); + }; + + Uint8.encode = function(encoder, val) { + encoder.writeUint8(val); + }; + + function Int16() { + } + + Int16.encodedSize = 2; + + Int16.decode = function(decoder) { + return decoder.readInt16(); + }; + + Int16.encode = function(encoder, val) { + encoder.writeInt16(val); + }; + + function Uint16() { + } + + Uint16.encodedSize = 2; + + Uint16.decode = function(decoder) { + return decoder.readUint16(); + }; + + Uint16.encode = function(encoder, val) { + encoder.writeUint16(val); + }; + + function Int32() { + } + + Int32.encodedSize = 4; + + Int32.decode = function(decoder) { + return decoder.readInt32(); + }; + + Int32.encode = function(encoder, val) { + encoder.writeInt32(val); + }; + + function Uint32() { + } + + Uint32.encodedSize = 4; + + Uint32.decode = function(decoder) { + return decoder.readUint32(); + }; + + Uint32.encode = function(encoder, val) { + encoder.writeUint32(val); + }; + + function Int64() { + } + + Int64.encodedSize = 8; + + Int64.decode = function(decoder) { + return decoder.readInt64(); + }; + + Int64.encode = function(encoder, val) { + encoder.writeInt64(val); + }; + + function Uint64() { + } + + Uint64.encodedSize = 8; + + Uint64.decode = function(decoder) { + return decoder.readUint64(); + }; + + Uint64.encode = function(encoder, val) { + encoder.writeUint64(val); + }; + + function String() { + }; + + String.encodedSize = 8; + + String.decode = function(decoder) { + return decoder.decodeStringPointer(); + }; + + String.encode = function(encoder, val) { + encoder.encodeStringPointer(val); + }; + + function NullableString() { + } + + NullableString.encodedSize = String.encodedSize; + + NullableString.decode = String.decode; + + NullableString.encode = String.encode; + + function Float() { + } + + Float.encodedSize = 4; + + Float.decode = function(decoder) { + return decoder.readFloat(); + }; + + Float.encode = function(encoder, val) { + encoder.writeFloat(val); + }; + + function Double() { + } + + Double.encodedSize = 8; + + Double.decode = function(decoder) { + return decoder.readDouble(); + }; + + Double.encode = function(encoder, val) { + encoder.writeDouble(val); + }; + + function PointerTo(cls) { + this.cls = cls; + } + + PointerTo.prototype.encodedSize = 8; + + PointerTo.prototype.decode = function(decoder) { + var pointer = decoder.decodePointer(); + if (!pointer) { + return null; + } + return this.cls.decode(decoder.decodeAndCreateDecoder(pointer)); + }; + + PointerTo.prototype.encode = function(encoder, val) { + if (!val) { + encoder.encodePointer(val); + return; + } + var objectEncoder = encoder.createAndEncodeEncoder(this.cls.encodedSize); + this.cls.encode(objectEncoder, val); + }; + + function NullablePointerTo(cls) { + PointerTo.call(this, cls); + } + + NullablePointerTo.prototype = Object.create(PointerTo.prototype); + + function ArrayOf(cls, length) { + this.cls = cls; + this.length = length || 0; + } + + ArrayOf.prototype.encodedSize = 8; + + ArrayOf.prototype.dimensions = function() { + return [this.length].concat( + (this.cls instanceof ArrayOf) ? this.cls.dimensions() : []); + } + + ArrayOf.prototype.decode = function(decoder) { + return decoder.decodeArrayPointer(this.cls); + }; + + ArrayOf.prototype.encode = function(encoder, val) { + encoder.encodeArrayPointer(this.cls, val); + }; + + function NullableArrayOf(cls) { + ArrayOf.call(this, cls); + } + + NullableArrayOf.prototype = Object.create(ArrayOf.prototype); + + function Handle() { + } + + Handle.encodedSize = 4; + + Handle.decode = function(decoder) { + return decoder.decodeHandle(); + }; + + Handle.encode = function(encoder, val) { + encoder.encodeHandle(val); + }; + + function NullableHandle() { + } + + NullableHandle.encodedSize = Handle.encodedSize; + + NullableHandle.decode = Handle.decode; + + NullableHandle.encode = Handle.encode; + + function Interface() { + } + + Interface.encodedSize = 8; + + Interface.decode = function(decoder) { + var handle = decoder.decodeHandle(); + // Ignore the version field for now. + decoder.readUint32(); + + return handle; + }; + + Interface.encode = function(encoder, val) { + encoder.encodeHandle(val); + // Set the version field to 0 for now. + encoder.writeUint32(0); + }; + + function NullableInterface() { + } + + NullableInterface.encodedSize = Interface.encodedSize; + + NullableInterface.decode = Interface.decode; + + NullableInterface.encode = Interface.encode; + + function MapOf(keyClass, valueClass) { + this.keyClass = keyClass; + this.valueClass = valueClass; + } + + MapOf.prototype.encodedSize = 8; + + MapOf.prototype.decode = function(decoder) { + return decoder.decodeMapPointer(this.keyClass, this.valueClass); + }; + + MapOf.prototype.encode = function(encoder, val) { + encoder.encodeMapPointer(this.keyClass, this.valueClass, val); + }; + + function NullableMapOf(keyClass, valueClass) { + MapOf.call(this, keyClass, valueClass); + } + + NullableMapOf.prototype = Object.create(MapOf.prototype); + + var exports = {}; + exports.align = align; + exports.isAligned = isAligned; + exports.Message = Message; + exports.MessageBuilder = MessageBuilder; + exports.MessageWithRequestIDBuilder = MessageWithRequestIDBuilder; + exports.MessageReader = MessageReader; + exports.kArrayHeaderSize = kArrayHeaderSize; + exports.kMapStructPayloadSize = kMapStructPayloadSize; + exports.kStructHeaderSize = kStructHeaderSize; + exports.kEncodedInvalidHandleValue = kEncodedInvalidHandleValue; + exports.kMessageHeaderSize = kMessageHeaderSize; + exports.kMessageWithRequestIDHeaderSize = kMessageWithRequestIDHeaderSize; + exports.kMessageExpectsResponse = kMessageExpectsResponse; + exports.kMessageIsResponse = kMessageIsResponse; + exports.Int8 = Int8; + exports.Uint8 = Uint8; + exports.Int16 = Int16; + exports.Uint16 = Uint16; + exports.Int32 = Int32; + exports.Uint32 = Uint32; + exports.Int64 = Int64; + exports.Uint64 = Uint64; + exports.Float = Float; + exports.Double = Double; + exports.String = String; + exports.NullableString = NullableString; + exports.PointerTo = PointerTo; + exports.NullablePointerTo = NullablePointerTo; + exports.ArrayOf = ArrayOf; + exports.NullableArrayOf = NullableArrayOf; + exports.PackedBool = PackedBool; + exports.Handle = Handle; + exports.NullableHandle = NullableHandle; + exports.Interface = Interface; + exports.NullableInterface = NullableInterface; + exports.MapOf = MapOf; + exports.NullableMapOf = NullableMapOf; + return exports; +}); diff --git a/mojo/public/js/codec_unittests.js b/mojo/public/js/codec_unittests.js new file mode 100644 index 0000000..b610d9a --- /dev/null +++ b/mojo/public/js/codec_unittests.js @@ -0,0 +1,296 @@ +// 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. + +define([ + "gin/test/expect", + "mojo/public/js/codec", + "mojo/public/interfaces/bindings/tests/rect.mojom", + "mojo/public/interfaces/bindings/tests/sample_service.mojom", + "mojo/public/interfaces/bindings/tests/test_structs.mojom", + ], function(expect, codec, rect, sample, structs) { + testBar(); + testFoo(); + testNamedRegion(); + testTypes(); + testAlign(); + testUtf8(); + testTypedPointerValidation(); + this.result = "PASS"; + + function testBar() { + var bar = new sample.Bar(); + bar.alpha = 1; + bar.beta = 2; + bar.gamma = 3; + bar.type = 0x08070605; + bar.extraProperty = "banana"; + + var messageName = 42; + var payloadSize = sample.Bar.encodedSize; + + var builder = new codec.MessageBuilder(messageName, payloadSize); + builder.encodeStruct(sample.Bar, bar); + + var message = builder.finish(); + + var expectedMemory = new Uint8Array([ + 24, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 42, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + + 16, 0, 0, 0, + 0, 0, 0, 0, + + 1, 2, 3, 0, + 5, 6, 7, 8, + ]); + + var actualMemory = new Uint8Array(message.buffer.arrayBuffer); + expect(actualMemory).toEqual(expectedMemory); + + var reader = new codec.MessageReader(message); + + expect(reader.payloadSize).toBe(payloadSize); + expect(reader.messageName).toBe(messageName); + + var bar2 = reader.decodeStruct(sample.Bar); + + expect(bar2.alpha).toBe(bar.alpha); + expect(bar2.beta).toBe(bar.beta); + expect(bar2.gamma).toBe(bar.gamma); + expect("extraProperty" in bar2).toBeFalsy(); + } + + function testFoo() { + var foo = new sample.Foo(); + foo.x = 0x212B4D5; + foo.y = 0x16E93; + foo.a = 1; + foo.b = 0; + foo.c = 3; // This will get truncated to one bit. + foo.bar = new sample.Bar(); + foo.bar.alpha = 91; + foo.bar.beta = 82; + foo.bar.gamma = 73; + foo.data = [ + 4, 5, 6, 7, 8, + ]; + foo.extra_bars = [ + new sample.Bar(), new sample.Bar(), new sample.Bar(), + ]; + for (var i = 0; i < foo.extra_bars.length; ++i) { + foo.extra_bars[i].alpha = 1 * i; + foo.extra_bars[i].beta = 2 * i; + foo.extra_bars[i].gamma = 3 * i; + } + foo.name = "I am a banana"; + // This is supposed to be a handle, but we fake it with an integer. + foo.source = 23423782; + foo.array_of_array_of_bools = [ + [true], [false, true] + ]; + foo.array_of_bools = [ + true, false, true, false, true, false, true, true + ]; + + + var messageName = 31; + var payloadSize = 304; + + var builder = new codec.MessageBuilder(messageName, payloadSize); + builder.encodeStruct(sample.Foo, foo); + + var message = builder.finish(); + + var expectedMemory = new Uint8Array([ + /* 0: */ 24, 0, 0, 0, 0, 0, 0, 0, + /* 8: */ 0, 0, 0, 0, 31, 0, 0, 0, + /* 16: */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 24: */ 96, 0, 0, 0, 0, 0, 0, 0, + /* 32: */ 0xD5, 0xB4, 0x12, 0x02, 0x93, 0x6E, 0x01, 0, + /* 40: */ 5, 0, 0, 0, 0, 0, 0, 0, + /* 48: */ 72, 0, 0, 0, 0, 0, 0, 0, + ]); + // TODO(abarth): Test more of the message's raw memory. + var actualMemory = new Uint8Array(message.buffer.arrayBuffer, + 0, expectedMemory.length); + expect(actualMemory).toEqual(expectedMemory); + + var expectedHandles = [ + 23423782, + ]; + + expect(message.handles).toEqual(expectedHandles); + + var reader = new codec.MessageReader(message); + + expect(reader.payloadSize).toBe(payloadSize); + expect(reader.messageName).toBe(messageName); + + var foo2 = reader.decodeStruct(sample.Foo); + + expect(foo2.x).toBe(foo.x); + expect(foo2.y).toBe(foo.y); + + expect(foo2.a).toBe(foo.a & 1 ? true : false); + expect(foo2.b).toBe(foo.b & 1 ? true : false); + expect(foo2.c).toBe(foo.c & 1 ? true : false); + + expect(foo2.bar).toEqual(foo.bar); + expect(foo2.data).toEqual(foo.data); + + expect(foo2.extra_bars).toEqual(foo.extra_bars); + expect(foo2.name).toBe(foo.name); + expect(foo2.source).toEqual(foo.source); + + expect(foo2.array_of_bools).toEqual(foo.array_of_bools); + } + + function createRect(x, y, width, height) { + var r = new rect.Rect(); + r.x = x; + r.y = y; + r.width = width; + r.height = height; + return r; + } + + // Verify that the references to the imported Rect type in test_structs.mojom + // are generated correctly. + function testNamedRegion() { + var r = new structs.NamedRegion(); + r.name = "rectangle"; + r.rects = new Array(createRect(1, 2, 3, 4), createRect(10, 20, 30, 40)); + + var builder = new codec.MessageBuilder(1, structs.NamedRegion.encodedSize); + builder.encodeStruct(structs.NamedRegion, r); + var reader = new codec.MessageReader(builder.finish()); + var result = reader.decodeStruct(structs.NamedRegion); + + expect(result.name).toEqual("rectangle"); + expect(result.rects[0]).toEqual(createRect(1, 2, 3, 4)); + expect(result.rects[1]).toEqual(createRect(10, 20, 30, 40)); + } + + function testTypes() { + function encodeDecode(cls, input, expectedResult, encodedSize) { + var messageName = 42; + var payloadSize = encodedSize || cls.encodedSize; + + var builder = new codec.MessageBuilder(messageName, payloadSize); + builder.encodeStruct(cls, input) + var message = builder.finish(); + + var reader = new codec.MessageReader(message); + expect(reader.payloadSize).toBe(payloadSize); + expect(reader.messageName).toBe(messageName); + var result = reader.decodeStruct(cls); + expect(result).toEqual(expectedResult); + } + encodeDecode(codec.String, "banana", "banana", 24); + encodeDecode(codec.NullableString, null, null, 8); + encodeDecode(codec.Int8, -1, -1); + encodeDecode(codec.Int8, 0xff, -1); + encodeDecode(codec.Int16, -1, -1); + encodeDecode(codec.Int16, 0xff, 0xff); + encodeDecode(codec.Int16, 0xffff, -1); + encodeDecode(codec.Int32, -1, -1); + encodeDecode(codec.Int32, 0xffff, 0xffff); + encodeDecode(codec.Int32, 0xffffffff, -1); + encodeDecode(codec.Float, 1.0, 1.0); + encodeDecode(codec.Double, 1.0, 1.0); + } + + function testAlign() { + var aligned = [ + 0, // 0 + 8, // 1 + 8, // 2 + 8, // 3 + 8, // 4 + 8, // 5 + 8, // 6 + 8, // 7 + 8, // 8 + 16, // 9 + 16, // 10 + 16, // 11 + 16, // 12 + 16, // 13 + 16, // 14 + 16, // 15 + 16, // 16 + 24, // 17 + 24, // 18 + 24, // 19 + 24, // 20 + ]; + for (var i = 0; i < aligned.length; ++i) + expect(codec.align(i)).toBe(aligned[i]); + } + + function testUtf8() { + var str = "B\u03ba\u1f79"; // some UCS-2 codepoints + var messageName = 42; + var payloadSize = 24; + + var builder = new codec.MessageBuilder(messageName, payloadSize); + var encoder = builder.createEncoder(8); + encoder.encodeStringPointer(str); + var message = builder.finish(); + var expectedMemory = new Uint8Array([ + /* 0: */ 24, 0, 0, 0, 0, 0, 0, 0, + /* 8: */ 0, 0, 0, 0, 42, 0, 0, 0, + /* 16: */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 24: */ 8, 0, 0, 0, 0, 0, 0, 0, + /* 32: */ 14, 0, 0, 0, 6, 0, 0, 0, + /* 40: */ 0x42, 0xCE, 0xBA, 0xE1, 0xBD, 0xB9, 0, 0, + ]); + var actualMemory = new Uint8Array(message.buffer.arrayBuffer); + expect(actualMemory.length).toEqual(expectedMemory.length); + expect(actualMemory).toEqual(expectedMemory); + + var reader = new codec.MessageReader(message); + expect(reader.payloadSize).toBe(payloadSize); + expect(reader.messageName).toBe(messageName); + var str2 = reader.decoder.decodeStringPointer(); + expect(str2).toEqual(str); + } + + function testTypedPointerValidation() { + var encoder = new codec.MessageBuilder(42, 24).createEncoder(8); + function DummyClass() {}; + var testCases = [ + // method, args, invalid examples, valid examples + [encoder.encodeArrayPointer, [DummyClass], [75], + [[], null, undefined, new Uint8Array([])]], + [encoder.encodeStringPointer, [], [75, new String("foo")], + ["", "bar", null, undefined]], + [encoder.encodeMapPointer, [DummyClass, DummyClass], [75], + [new Map(), null, undefined]], + ]; + + testCases.forEach(function(test) { + var method = test[0]; + var baseArgs = test[1]; + var invalidExamples = test[2]; + var validExamples = test[3]; + + var encoder = new codec.MessageBuilder(42, 24).createEncoder(8); + invalidExamples.forEach(function(invalid) { + expect(function() { + method.apply(encoder, baseArgs.concat(invalid)); + }).toThrow(); + }); + + validExamples.forEach(function(valid) { + var encoder = new codec.MessageBuilder(42, 24).createEncoder(8); + method.apply(encoder, baseArgs.concat(valid)); + }); + }); + } +}); diff --git a/mojo/public/js/connection.js b/mojo/public/js/connection.js new file mode 100644 index 0000000..62e8798 --- /dev/null +++ b/mojo/public/js/connection.js @@ -0,0 +1,155 @@ +// 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. + +define("mojo/public/js/connection", [ + "mojo/public/js/bindings", + "mojo/public/js/connector", + "mojo/public/js/core", + "mojo/public/js/router", +], function(bindings, connector, core, router) { + + var Router = router.Router; + var EmptyProxy = bindings.EmptyProxy; + var EmptyStub = bindings.EmptyStub; + var ProxyBindings = bindings.ProxyBindings; + var StubBindings = bindings.StubBindings; + var TestConnector = connector.TestConnector; + var TestRouter = router.TestRouter; + + // TODO(hansmuller): the proxy receiver_ property should be receiver$ + + function BaseConnection(localStub, remoteProxy, router) { + this.router_ = router; + this.local = localStub; + this.remote = remoteProxy; + + this.router_.setIncomingReceiver(localStub); + if (this.remote) + this.remote.receiver_ = router; + + // Validate incoming messages: remote responses and local requests. + var validateRequest = localStub && localStub.validator; + var validateResponse = remoteProxy && remoteProxy.validator; + var payloadValidators = []; + if (validateRequest) + payloadValidators.push(validateRequest); + if (validateResponse) + payloadValidators.push(validateResponse); + this.router_.setPayloadValidators(payloadValidators); + } + + BaseConnection.prototype.close = function() { + this.router_.close(); + this.router_ = null; + this.local = null; + this.remote = null; + }; + + BaseConnection.prototype.encounteredError = function() { + return this.router_.encounteredError(); + }; + + function Connection( + handle, localFactory, remoteFactory, routerFactory, connectorFactory) { + var routerClass = routerFactory || Router; + var router = new routerClass(handle, connectorFactory); + var remoteProxy = remoteFactory && new remoteFactory(router); + var localStub = localFactory && new localFactory(remoteProxy); + BaseConnection.call(this, localStub, remoteProxy, router); + } + + Connection.prototype = Object.create(BaseConnection.prototype); + + // The TestConnection subclass is only intended to be used in unit tests. + function TestConnection(handle, localFactory, remoteFactory) { + Connection.call(this, + handle, + localFactory, + remoteFactory, + TestRouter, + TestConnector); + } + + TestConnection.prototype = Object.create(Connection.prototype); + + // Return a handle for a message pipe that's connected to a proxy + // for remoteInterface. Used by generated code for outgoing interface& + // (request) parameters: the caller is given the generated proxy via + // |proxyCallback(proxy)| and the generated code sends the handle + // returned by this function. + function bindProxy(proxyCallback, remoteInterface) { + var messagePipe = core.createMessagePipe(); + if (messagePipe.result != core.RESULT_OK) + throw new Error("createMessagePipe failed " + messagePipe.result); + + var proxy = new remoteInterface.proxyClass; + var router = new Router(messagePipe.handle0); + var connection = new BaseConnection(undefined, proxy, router); + ProxyBindings(proxy).connection = connection; + if (proxyCallback) + proxyCallback(proxy); + + return messagePipe.handle1; + } + + // Return a handle for a message pipe that's connected to a stub for + // localInterface. Used by generated code for outgoing interface + // parameters: the caller is given the generated stub via + // |stubCallback(stub)| and the generated code sends the handle + // returned by this function. The caller is responsible for managing + // the lifetime of the stub and for setting it's implementation + // delegate with: StubBindings(stub).delegate = myImpl; + function bindImpl(stubCallback, localInterface) { + var messagePipe = core.createMessagePipe(); + if (messagePipe.result != core.RESULT_OK) + throw new Error("createMessagePipe failed " + messagePipe.result); + + var stub = new localInterface.stubClass; + var router = new Router(messagePipe.handle0); + var connection = new BaseConnection(stub, undefined, router); + StubBindings(stub).connection = connection; + if (stubCallback) + stubCallback(stub); + + return messagePipe.handle1; + } + + // Return a remoteInterface proxy for handle. Used by generated code + // for converting incoming interface parameters to proxies. + function bindHandleToProxy(handle, remoteInterface) { + if (!core.isHandle(handle)) + throw new Error("Not a handle " + handle); + + var proxy = new remoteInterface.proxyClass; + var router = new Router(handle); + var connection = new BaseConnection(undefined, proxy, router); + ProxyBindings(proxy).connection = connection; + return proxy; + } + + // Return a localInterface stub for handle. Used by generated code + // for converting incoming interface& request parameters to localInterface + // stubs. The caller can specify the stub's implementation of localInterface + // like this: StubBindings(stub).delegate = myStubImpl. + function bindHandleToStub(handle, localInterface) { + if (!core.isHandle(handle)) + throw new Error("Not a handle " + handle); + + var stub = new localInterface.stubClass; + var router = new Router(handle); + var connection = new BaseConnection(stub, undefined, router); + StubBindings(stub).connection = connection; + return stub; + } + + var exports = {}; + exports.Connection = Connection; + exports.TestConnection = TestConnection; + + exports.bindProxy = bindProxy; + exports.bindImpl = bindImpl; + exports.bindHandleToProxy = bindHandleToProxy; + exports.bindHandleToStub = bindHandleToStub; + return exports; +}); diff --git a/mojo/public/js/connector.js b/mojo/public/js/connector.js new file mode 100644 index 0000000..78ed963 --- /dev/null +++ b/mojo/public/js/connector.js @@ -0,0 +1,132 @@ +// 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. + +define("mojo/public/js/connector", [ + "mojo/public/js/buffer", + "mojo/public/js/codec", + "mojo/public/js/core", + "mojo/public/js/support", +], function(buffer, codec, core, support) { + + function Connector(handle) { + if (!core.isHandle(handle)) + throw new Error("Connector: not a handle " + handle); + this.handle_ = handle; + this.dropWrites_ = false; + this.error_ = false; + this.incomingReceiver_ = null; + this.readWaitCookie_ = null; + this.errorHandler_ = null; + + if (handle) + this.waitToReadMore_(); + } + + Connector.prototype.close = function() { + if (this.readWaitCookie_) { + support.cancelWait(this.readWaitCookie_); + this.readWaitCookie_ = null; + } + if (this.handle_ != null) { + core.close(this.handle_); + this.handle_ = null; + } + }; + + Connector.prototype.accept = function(message) { + if (this.error_) + return false; + + if (this.dropWrites_) + return true; + + var result = core.writeMessage(this.handle_, + new Uint8Array(message.buffer.arrayBuffer), + message.handles, + core.WRITE_MESSAGE_FLAG_NONE); + switch (result) { + case core.RESULT_OK: + // The handles were successfully transferred, so we don't own them + // anymore. + message.handles = []; + break; + case core.RESULT_FAILED_PRECONDITION: + // There's no point in continuing to write to this pipe since the other + // end is gone. Avoid writing any future messages. Hide write failures + // from the caller since we'd like them to continue consuming any + // backlog of incoming messages before regarding the message pipe as + // closed. + this.dropWrites_ = true; + break; + default: + // This particular write was rejected, presumably because of bad input. + // The pipe is not necessarily in a bad state. + return false; + } + return true; + }; + + Connector.prototype.setIncomingReceiver = function(receiver) { + this.incomingReceiver_ = receiver; + }; + + Connector.prototype.setErrorHandler = function(handler) { + this.errorHandler_ = handler; + }; + + Connector.prototype.encounteredError = function() { + return this.error_; + }; + + Connector.prototype.waitToReadMore_ = function() { + this.readWaitCookie_ = support.asyncWait(this.handle_, + core.HANDLE_SIGNAL_READABLE, + this.readMore_.bind(this)); + }; + + Connector.prototype.readMore_ = function(result) { + for (;;) { + var read = core.readMessage(this.handle_, + core.READ_MESSAGE_FLAG_NONE); + if (this.handle_ == null) // The connector has been closed. + return; + if (read.result == core.RESULT_SHOULD_WAIT) { + this.waitToReadMore_(); + return; + } + if (read.result != core.RESULT_OK) { + this.error_ = true; + if (this.errorHandler_) + this.errorHandler_.onError(read.result); + return; + } + var messageBuffer = new buffer.Buffer(read.buffer); + var message = new codec.Message(messageBuffer, read.handles); + if (this.incomingReceiver_) { + this.incomingReceiver_.accept(message); + } + } + }; + + // The TestConnector subclass is only intended to be used in unit tests. It + // enables delivering a message to the pipe's handle without an async wait. + + function TestConnector(handle) { + Connector.call(this, handle); + } + + TestConnector.prototype = Object.create(Connector.prototype); + + TestConnector.prototype.waitToReadMore_ = function() { + }; + + TestConnector.prototype.deliverMessage = function() { + this.readMore_(core.RESULT_OK); + } + + var exports = {}; + exports.Connector = Connector; + exports.TestConnector = TestConnector; + return exports; +}); diff --git a/mojo/public/js/constants.cc b/mojo/public/js/constants.cc new file mode 100644 index 0000000..d29f5cb --- /dev/null +++ b/mojo/public/js/constants.cc @@ -0,0 +1,18 @@ +// 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. + +#include "mojo/public/js/constants.h" + +namespace mojo { + +const char kBindingsModuleName[] = "mojo/public/js/bindings"; +const char kBufferModuleName[] = "mojo/public/js/buffer"; +const char kCodecModuleName[] = "mojo/public/js/codec"; +const char kConnectionModuleName[] = "mojo/public/js/connection"; +const char kConnectorModuleName[] = "mojo/public/js/connector"; +const char kUnicodeModuleName[] = "mojo/public/js/unicode"; +const char kRouterModuleName[] = "mojo/public/js/router"; +const char kValidatorModuleName[] = "mojo/public/js/validator"; + +} // namespace mojo diff --git a/mojo/public/js/constants.h b/mojo/public/js/constants.h new file mode 100644 index 0000000..de75a90 --- /dev/null +++ b/mojo/public/js/constants.h @@ -0,0 +1,22 @@ +// 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. + +#ifndef MOJO_PUBLIC_JS_BINDINGS_CONSTANTS_H_ +#define MOJO_PUBLIC_JS_BINDINGS_CONSTANTS_H_ + +namespace mojo { + +// JavaScript module names: +extern const char kBindingsModuleName[]; +extern const char kBufferModuleName[]; +extern const char kCodecModuleName[]; +extern const char kConnectionModuleName[]; +extern const char kConnectorModuleName[]; +extern const char kUnicodeModuleName[]; +extern const char kRouterModuleName[]; +extern const char kValidatorModuleName[]; + +} // namespace mojo + +#endif // MOJO_PUBLIC_JS_BINDINGS_CONSTANTS_H_ diff --git a/mojo/public/js/core.js b/mojo/public/js/core.js new file mode 100644 index 0000000..b89a956 --- /dev/null +++ b/mojo/public/js/core.js @@ -0,0 +1,238 @@ +// 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. + +// Module "mojo/public/js/core" +// +// 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 the JavaScript bindings for mojo/public/c/system/core.h. +// Refer to that file for more detailed documentation for equivalent methods. + +while (1); + +/** + * MojoHandle: An opaque handles to a Mojo object (e.g. a message pipe). + */ +var kInvalidHandle; + +/** + * MojoResult {number}: Result codes for Mojo operations. + * See core.h for more information. + */ +var RESULT_OK; +var RESULT_CANCELLED; +var RESULT_UNKNOWN; +var RESULT_INVALID_ARGUMENT; +var RESULT_DEADLINE_EXCEEDED; +var RESULT_NOT_FOUND; +var RESULT_ALREADY_EXISTS; +var RESULT_PERMISSION_DENIED; +var RESULT_RESOURCE_EXHAUSTED; +var RESULT_FAILED_PRECONDITION; +var RESULT_ABORTED; +var RESULT_OUT_OF_RANGE; +var RESULT_UNIMPLEMENTED; +var RESULT_INTERNAL; +var RESULT_UNAVAILABLE; +var RESULT_DATA_LOSS; +var RESULT_BUSY; +var RESULT_SHOULD_WAIT; + +/** + * MojoDeadline {number}: Used to specify deadlines (timeouts), in microseconds. + * See core.h for more information. + */ +var DEADLINE_INDEFINITE; + +/** + * MojoHandleSignals: Used to specify signals that can be waited on for a handle + *(and which can be triggered), e.g., the ability to read or write to + * the handle. + * See core.h for more information. + */ +var HANDLE_SIGNAL_NONE; +var HANDLE_SIGNAL_READABLE; +var HANDLE_SIGNAL_WRITABLE; +var HANDLE_SIGNAL_PEER_CLOSED; + +/** + * MojoCreateDataMessageOptions: Used to specify creation parameters for a data + * pipe to |createDataMessage()|. + * See core.h for more information. + */ +dictionary MojoCreateDataMessageOptions { + MojoCreateDataMessageOptionsFlags flags; // See below. +}; + +// MojoCreateDataMessageOptionsFlags +var CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE; + +/* + * MojoWriteMessageFlags: Used to specify different modes to |writeMessage()|. + * See core.h for more information. + */ +var WRITE_MESSAGE_FLAG_NONE; + +/** + * MojoReadMessageFlags: Used to specify different modes to |readMessage()|. + * See core.h for more information. + */ +var READ_MESSAGE_FLAG_NONE; +var READ_MESSAGE_FLAG_MAY_DISCARD; + +/** + * MojoCreateDataPipeOptions: Used to specify creation parameters for a data + * pipe to |createDataPipe()|. + * See core.h for more information. + */ +dictionary MojoCreateDataPipeOptions { + MojoCreateDataPipeOptionsFlags flags; // See below. + int32 elementNumBytes; // The size of an element, in bytes. + int32 capacityNumBytes; // The capacity of the data pipe, in bytes. +}; + +// MojoCreateDataPipeOptionsFlags +var CREATE_DATA_PIPE_OPTIONS_FLAG_NONE; + +/* + * MojoWriteDataFlags: Used to specify different modes to |writeData()|. + * See core.h for more information. + */ +var WRITE_DATA_FLAG_NONE; +var WRITE_DATA_FLAG_ALL_OR_NONE; + +/** + * MojoReadDataFlags: Used to specify different modes to |readData()|. + * See core.h for more information. + */ +var READ_DATA_FLAG_NONE; +var READ_DATA_FLAG_ALL_OR_NONE; +var READ_DATA_FLAG_DISCARD; +var READ_DATA_FLAG_QUERY; +var READ_DATA_FLAG_PEEK; + +/** + * Closes the given |handle|. See MojoClose for more info. + * @param {MojoHandle} Handle to close. + * @return {MojoResult} Result code. + */ +function close(handle) { [native code] } + +/** + * Waits on the given handle until a signal indicated by |signals| is + * satisfied or until |deadline| is passed. See MojoWait for more information. + * + * @param {MojoHandle} handle Handle to wait on. + * @param {MojoHandleSignals} signals Specifies the condition to wait for. + * @param {MojoDeadline} deadline Stops waiting if this is reached. + * @return {MojoResult} Result code. + */ +function wait(handle, signals, deadline) { [native code] } + +/** + * Waits on |handles[0]|, ..., |handles[handles.length-1]| for at least one of + * them to satisfy the state indicated by |flags[0]|, ..., + * |flags[handles.length-1]|, respectively, or until |deadline| has passed. + * See MojoWaitMany for more information. + * + * @param {Array.MojoHandle} handles Handles to wait on. + * @param {Array.MojoHandleSignals} signals Specifies the condition to wait for, + * for each corresponding handle. Must be the same length as |handles|. + * @param {MojoDeadline} deadline Stops waiting if this is reached. + * @return {MojoResult} Result code. + */ +function waitMany(handles, signals, deadline) { [native code] } + +/** + * Creates a message pipe. This function always succeeds. + * See MojoCreateMessagePipe for more information on message pipes. + * + * @param {MojoCreateMessagePipeOptions} optionsDict Options to control the + * message pipe parameters. May be null. + * @return {MessagePipe} An object of the form { + * handle0, + * handle1, + * } + * where |handle0| and |handle1| are MojoHandles to each end of the channel. + */ +function createMessagePipe(optionsDict) { [native code] } + +/** + * Writes a message to the message pipe endpoint given by |handle|. See + * MojoWriteMessage for more information, including return codes. + * + * @param {MojoHandle} handle The endpoint to write to. + * @param {ArrayBufferView} buffer The message data. May be empty. + * @param {Array.MojoHandle} handlesArray Any handles to attach. Handles are + * transferred on success and will no longer be valid. May be empty. + * @param {MojoWriteMessageFlags} flags Flags. + * @return {MojoResult} Result code. + */ +function writeMessage(handle, buffer, handlesArray, flags) { [native code] } + +/** + * Reads a message from the message pipe endpoint given by |handle|. See + * MojoReadMessage for more information, including return codes. + * + * @param {MojoHandle} handle The endpoint to read from. + * @param {MojoReadMessageFlags} flags Flags. + * @return {object} An object of the form { + * result, // |RESULT_OK| on success, error code otherwise. + * buffer, // An ArrayBufferView of the message data (only on success). + * handles // An array of MojoHandles transferred, if any. + * } + */ +function readMessage(handle, flags) { [native code] } + +/** + * Creates a data pipe, which is a unidirectional communication channel for + * unframed data, with the given options. See MojoCreateDataPipe for more + * more information, including return codes. + * + * @param {MojoCreateDataPipeOptions} optionsDict Options to control the data + * pipe parameters. May be null. + * @return {object} An object of the form { + * result, // |RESULT_OK| on success, error code otherwise. + * producerHandle, // MojoHandle to use with writeData (only on success). + * consumerHandle, // MojoHandle to use with readData (only on success). + * } + */ +function createDataPipe(optionsDict) { [native code] } + +/** + * Writes the given data to the data pipe producer given by |handle|. See + * MojoWriteData for more information, including return codes. + * + * @param {MojoHandle} handle A producerHandle returned by createDataPipe. + * @param {ArrayBufferView} buffer The data to write. + * @param {MojoWriteDataFlags} flags Flags. + * @return {object} An object of the form { + * result, // |RESULT_OK| on success, error code otherwise. + * numBytes, // The number of bytes written. + * } + */ +function writeData(handle, buffer, flags) { [native code] } + +/** + * Reads data from the data pipe consumer given by |handle|. May also + * be used to discard data. See MojoReadData for more information, including + * return codes. + * + * @param {MojoHandle} handle A consumerHandle returned by createDataPipe. + * @param {MojoReadDataFlags} flags Flags. + * @return {object} An object of the form { + * result, // |RESULT_OK| on success, error code otherwise. + * buffer, // An ArrayBufferView of the data read (only on success). + * } + */ +function readData(handle, flags) { [native code] } + +/** + * True if the argument is a message or data pipe handle. + * + * @param {value} an arbitrary JS value. + * @return true or false + */ +function isHandle(value) { [native code] } diff --git a/mojo/public/js/core_unittests.js b/mojo/public/js/core_unittests.js new file mode 100644 index 0000000..0f7b3ff --- /dev/null +++ b/mojo/public/js/core_unittests.js @@ -0,0 +1,197 @@ +// 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. + +define([ + "gin/test/expect", + "mojo/public/js/core", + "gc", + ], function(expect, core, gc) { + + var HANDLE_SIGNAL_READWRITABLE = core.HANDLE_SIGNAL_WRITABLE | + core.HANDLE_SIGNAL_READABLE; + var HANDLE_SIGNAL_ALL = core.HANDLE_SIGNAL_WRITABLE | + core.HANDLE_SIGNAL_READABLE | + core.HANDLE_SIGNAL_PEER_CLOSED; + + runWithMessagePipe(testNop); + runWithMessagePipe(testReadAndWriteMessage); + runWithMessagePipeWithOptions(testNop); + runWithMessagePipeWithOptions(testReadAndWriteMessage); + runWithDataPipe(testNop); + runWithDataPipe(testReadAndWriteDataPipe); + runWithDataPipeWithOptions(testNop); + runWithDataPipeWithOptions(testReadAndWriteDataPipe); + runWithMessagePipe(testIsHandleMessagePipe); + runWithDataPipe(testIsHandleDataPipe); + gc.collectGarbage(); // should not crash + this.result = "PASS"; + + function runWithMessagePipe(test) { + var pipe = core.createMessagePipe(); + expect(pipe.result).toBe(core.RESULT_OK); + + test(pipe); + + expect(core.close(pipe.handle0)).toBe(core.RESULT_OK); + expect(core.close(pipe.handle1)).toBe(core.RESULT_OK); + } + + function runWithMessagePipeWithOptions(test) { + var pipe = core.createMessagePipe({ + flags: core.CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE + }); + expect(pipe.result).toBe(core.RESULT_OK); + + test(pipe); + + expect(core.close(pipe.handle0)).toBe(core.RESULT_OK); + expect(core.close(pipe.handle1)).toBe(core.RESULT_OK); + } + + function runWithDataPipe(test) { + var pipe = core.createDataPipe(); + expect(pipe.result).toBe(core.RESULT_OK); + + test(pipe); + + expect(core.close(pipe.producerHandle)).toBe(core.RESULT_OK); + expect(core.close(pipe.consumerHandle)).toBe(core.RESULT_OK); + } + + function runWithDataPipeWithOptions(test) { + var pipe = core.createDataPipe({ + flags: core.CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, + elementNumBytes: 1, + capacityNumBytes: 64 + }); + expect(pipe.result).toBe(core.RESULT_OK); + + test(pipe); + + expect(core.close(pipe.producerHandle)).toBe(core.RESULT_OK); + expect(core.close(pipe.consumerHandle)).toBe(core.RESULT_OK); + } + + function testNop(pipe) { + } + + function testReadAndWriteMessage(pipe) { + var wait = core.waitMany([], [], 0); + expect(wait.result).toBe(core.RESULT_INVALID_ARGUMENT); + expect(wait.index).toBe(null); + expect(wait.signalsState).toBe(null); + + wait = core.wait(pipe.handle0, core.HANDLE_SIGNAL_READABLE, 0); + expect(wait.result).toBe(core.RESULT_DEADLINE_EXCEEDED); + expect(wait.signalsState.satisfiedSignals).toBe( + core.HANDLE_SIGNAL_WRITABLE); + expect(wait.signalsState.satisfiableSignals).toBe(HANDLE_SIGNAL_ALL); + + wait = core.waitMany( + [pipe.handle0, pipe.handle1], + [core.HANDLE_SIGNAL_READABLE,core.HANDLE_SIGNAL_READABLE], + 0); + expect(wait.result).toBe(core.RESULT_DEADLINE_EXCEEDED); + expect(wait.index).toBe(null); + expect(wait.signalsState[0].satisfiedSignals).toBe( + core.HANDLE_SIGNAL_WRITABLE); + expect(wait.signalsState[0].satisfiableSignals).toBe(HANDLE_SIGNAL_ALL); + expect(wait.signalsState[1].satisfiedSignals).toBe( + core.HANDLE_SIGNAL_WRITABLE); + expect(wait.signalsState[1].satisfiableSignals).toBe(HANDLE_SIGNAL_ALL); + + wait = core.wait(pipe.handle0, core.HANDLE_SIGNAL_WRITABLE, 0); + expect(wait.result).toBe(core.RESULT_OK); + expect(wait.signalsState.satisfiedSignals).toBe( + core.HANDLE_SIGNAL_WRITABLE); + expect(wait.signalsState.satisfiableSignals).toBe(HANDLE_SIGNAL_ALL); + + var senderData = new Uint8Array(42); + for (var i = 0; i < senderData.length; ++i) { + senderData[i] = i * i; + } + + var result = core.writeMessage( + pipe.handle0, senderData, [], + core.WRITE_MESSAGE_FLAG_NONE); + + expect(result).toBe(core.RESULT_OK); + + wait = core.waitMany( + [pipe.handle0, pipe.handle1], + [core.HANDLE_SIGNAL_WRITABLE,core.HANDLE_SIGNAL_WRITABLE], + 0); + expect(wait.result).toBe(core.RESULT_OK); + expect(wait.index).toBe(0); + expect(wait.signalsState[0].satisfiedSignals).toBe( + core.HANDLE_SIGNAL_WRITABLE); + expect(wait.signalsState[0].satisfiableSignals).toBe(HANDLE_SIGNAL_ALL); + expect(wait.signalsState[1].satisfiedSignals).toBe( + HANDLE_SIGNAL_READWRITABLE); + expect(wait.signalsState[1].satisfiableSignals).toBe(HANDLE_SIGNAL_ALL); + + var read = core.readMessage( + pipe.handle1, core.READ_MESSAGE_FLAG_NONE); + + expect(read.result).toBe(core.RESULT_OK); + expect(read.buffer.byteLength).toBe(42); + expect(read.handles.length).toBe(0); + + var memory = new Uint8Array(read.buffer); + for (var i = 0; i < memory.length; ++i) + expect(memory[i]).toBe((i * i) & 0xFF); + } + + function testReadAndWriteDataPipe(pipe) { + var senderData = new Uint8Array(42); + for (var i = 0; i < senderData.length; ++i) { + senderData[i] = i * i; + } + + var write = core.writeData( + pipe.producerHandle, senderData, + core.WRITE_DATA_FLAG_ALL_OR_NONE); + + expect(write.result).toBe(core.RESULT_OK); + expect(write.numBytes).toBe(42); + + var peeked = core.readData( + pipe.consumerHandle, + core.READ_DATA_FLAG_PEEK | core.READ_DATA_FLAG_ALL_OR_NONE); + expect(peeked.result).toBe(core.RESULT_OK); + expect(peeked.buffer.byteLength).toBe(42); + + var peeked_memory = new Uint8Array(peeked.buffer); + for (var i = 0; i < peeked_memory.length; ++i) + expect(peeked_memory[i]).toBe((i * i) & 0xFF); + + var read = core.readData( + pipe.consumerHandle, core.READ_DATA_FLAG_ALL_OR_NONE); + + expect(read.result).toBe(core.RESULT_OK); + expect(read.buffer.byteLength).toBe(42); + + var memory = new Uint8Array(read.buffer); + for (var i = 0; i < memory.length; ++i) + expect(memory[i]).toBe((i * i) & 0xFF); + } + + function testIsHandleMessagePipe(pipe) { + expect(core.isHandle(123).toBeFalsy); + expect(core.isHandle("123").toBeFalsy); + expect(core.isHandle({}).toBeFalsy); + expect(core.isHandle([]).toBeFalsy); + expect(core.isHandle(undefined).toBeFalsy); + expect(core.isHandle(pipe).toBeFalsy); + expect(core.isHandle(pipe.handle0)).toBeTruthy(); + expect(core.isHandle(pipe.handle1)).toBeTruthy(); + expect(core.isHandle(null)).toBeTruthy(); + } + + function testIsHandleDataPipe(pipe) { + expect(core.isHandle(pipe.consumerHandle)).toBeTruthy(); + expect(core.isHandle(pipe.producerHandle)).toBeTruthy(); + } + +}); diff --git a/mojo/public/js/router.js b/mojo/public/js/router.js new file mode 100644 index 0000000..dba3e3c --- /dev/null +++ b/mojo/public/js/router.js @@ -0,0 +1,145 @@ +// 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. + +define("mojo/public/js/router", [ + "mojo/public/js/codec", + "mojo/public/js/core", + "mojo/public/js/connector", + "mojo/public/js/validator", +], function(codec, core, connector, validator) { + + var Connector = connector.Connector; + var MessageReader = codec.MessageReader; + var Validator = validator.Validator; + + function Router(handle, connectorFactory) { + if (!core.isHandle(handle)) + throw new Error("Router constructor: Not a handle"); + if (connectorFactory === undefined) + connectorFactory = Connector; + this.connector_ = new connectorFactory(handle); + this.incomingReceiver_ = null; + this.nextRequestID_ = 0; + this.completers_ = new Map(); + this.payloadValidators_ = []; + + this.connector_.setIncomingReceiver({ + accept: this.handleIncomingMessage_.bind(this), + }); + this.connector_.setErrorHandler({ + onError: this.handleConnectionError_.bind(this), + }); + } + + Router.prototype.close = function() { + this.completers_.clear(); // Drop any responders. + this.connector_.close(); + }; + + Router.prototype.accept = function(message) { + this.connector_.accept(message); + }; + + Router.prototype.reject = function(message) { + // TODO(mpcomplete): no way to trasmit errors over a Connection. + }; + + Router.prototype.acceptAndExpectResponse = function(message) { + // Reserve 0 in case we want it to convey special meaning in the future. + var requestID = this.nextRequestID_++; + if (requestID == 0) + requestID = this.nextRequestID_++; + + message.setRequestID(requestID); + var result = this.connector_.accept(message); + if (!result) + return Promise.reject(Error("Connection error")); + + var completer = {}; + this.completers_.set(requestID, completer); + return new Promise(function(resolve, reject) { + completer.resolve = resolve; + completer.reject = reject; + }); + }; + + Router.prototype.setIncomingReceiver = function(receiver) { + this.incomingReceiver_ = receiver; + }; + + Router.prototype.setPayloadValidators = function(payloadValidators) { + this.payloadValidators_ = payloadValidators; + }; + + Router.prototype.encounteredError = function() { + return this.connector_.encounteredError(); + }; + + Router.prototype.handleIncomingMessage_ = function(message) { + var noError = validator.validationError.NONE; + var messageValidator = new Validator(message); + var err = messageValidator.validateMessageHeader(); + for (var i = 0; err === noError && i < this.payloadValidators_.length; ++i) + err = this.payloadValidators_[i](messageValidator); + + if (err == noError) + this.handleValidIncomingMessage_(message); + else + this.handleInvalidIncomingMessage_(message, err); + }; + + Router.prototype.handleValidIncomingMessage_ = function(message) { + if (message.expectsResponse()) { + if (this.incomingReceiver_) { + this.incomingReceiver_.acceptWithResponder(message, this); + } else { + // If we receive a request expecting a response when the client is not + // listening, then we have no choice but to tear down the pipe. + this.close(); + } + } else if (message.isResponse()) { + var reader = new MessageReader(message); + var requestID = reader.requestID; + var completer = this.completers_.get(requestID); + this.completers_.delete(requestID); + completer.resolve(message); + } else { + if (this.incomingReceiver_) + this.incomingReceiver_.accept(message); + } + } + + Router.prototype.handleInvalidIncomingMessage_ = function(message, error) { + this.close(); + } + + Router.prototype.handleConnectionError_ = function(result) { + this.completers_.forEach(function(value) { + value.reject(result); + }); + this.close(); + }; + + // The TestRouter subclass is only intended to be used in unit tests. + // It defeats valid message handling and delgates invalid message handling. + + function TestRouter(handle, connectorFactory) { + Router.call(this, handle, connectorFactory); + } + + TestRouter.prototype = Object.create(Router.prototype); + + TestRouter.prototype.handleValidIncomingMessage_ = function() { + }; + + TestRouter.prototype.handleInvalidIncomingMessage_ = + function(message, error) { + this.validationErrorHandler(error); + }; + + var exports = {}; + exports.Router = Router; + exports.TestRouter = TestRouter; + return exports; +}); diff --git a/mojo/public/js/struct_unittests.js b/mojo/public/js/struct_unittests.js new file mode 100644 index 0000000..c0131be --- /dev/null +++ b/mojo/public/js/struct_unittests.js @@ -0,0 +1,280 @@ +// 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. + +define([ + "gin/test/expect", + "mojo/public/interfaces/bindings/tests/rect.mojom", + "mojo/public/interfaces/bindings/tests/test_structs.mojom", + "mojo/public/js/codec", + "mojo/public/js/validator", +], function(expect, + rect, + testStructs, + codec, + validator) { + + function testConstructors() { + var r = new rect.Rect(); + expect(r).toEqual(new rect.Rect({x:0, y:0, width:0, height:0})); + expect(r).toEqual(new rect.Rect({foo:100, bar:200})); + + r.x = 10; + r.y = 20; + r.width = 30; + r.height = 40; + var rp = new testStructs.RectPair({first: r, second: r}); + expect(rp.first).toEqual(r); + expect(rp.second).toEqual(r); + + expect(new testStructs.RectPair({second: r}).first).toBeNull(); + + var nr = new testStructs.NamedRegion(); + expect(nr.name).toBeNull(); + expect(nr.rects).toBeNull(); + expect(nr).toEqual(new testStructs.NamedRegion({})); + + nr.name = "foo"; + nr.rects = [r, r, r]; + expect(nr).toEqual(new testStructs.NamedRegion({ + name: "foo", + rects: [r, r, r], + })); + + var e = new testStructs.EmptyStruct(); + expect(e).toEqual(new testStructs.EmptyStruct({foo:123})); + } + + function testNoDefaultFieldValues() { + var s = new testStructs.NoDefaultFieldValues(); + expect(s.f0).toEqual(false); + + // f1 - f10, number type fields + for (var i = 1; i <= 10; i++) + expect(s["f" + i]).toEqual(0); + + // f11,12 strings, f13-22 handles, f23-f26 arrays, f27,28 structs + for (var i = 11; i <= 28; i++) + expect(s["f" + i]).toBeNull(); + } + + function testDefaultFieldValues() { + var s = new testStructs.DefaultFieldValues(); + expect(s.f0).toEqual(true); + + // f1 - f12, number type fields + for (var i = 1; i <= 12; i++) + expect(s["f" + i]).toEqual(100); + + // f13,14 "foo" + for (var i = 13; i <= 14; i++) + expect(s["f" + i]).toEqual("foo"); + + // f15,16 a default instance of Rect + var r = new rect.Rect(); + expect(s.f15).toEqual(r); + expect(s.f16).toEqual(r); + } + + function testScopedConstants() { + expect(testStructs.ScopedConstants.TEN).toEqual(10); + expect(testStructs.ScopedConstants.ALSO_TEN).toEqual(10); + expect(testStructs.ScopedConstants.TEN_TOO).toEqual(10); + + expect(testStructs.ScopedConstants.EType.E0).toEqual(0); + expect(testStructs.ScopedConstants.EType.E1).toEqual(1); + expect(testStructs.ScopedConstants.EType.E2).toEqual(10); + expect(testStructs.ScopedConstants.EType.E3).toEqual(10); + expect(testStructs.ScopedConstants.EType.E4).toEqual(11); + + var s = new testStructs.ScopedConstants(); + expect(s.f0).toEqual(0); + expect(s.f1).toEqual(1); + expect(s.f2).toEqual(10); + expect(s.f3).toEqual(10); + expect(s.f4).toEqual(11); + expect(s.f5).toEqual(10); + expect(s.f6).toEqual(10); + } + + function structEncodeDecode(struct) { + var structClass = struct.constructor; + var builder = new codec.MessageBuilder(1234, structClass.encodedSize); + builder.encodeStruct(structClass, struct); + var message = builder.finish(); + + var messageValidator = new validator.Validator(message); + var err = structClass.validate(messageValidator, codec.kMessageHeaderSize); + expect(err).toEqual(validator.validationError.NONE); + + var reader = new codec.MessageReader(message); + return reader.decodeStruct(structClass); + } + + function testMapKeyTypes() { + var mapFieldsStruct = new testStructs.MapKeyTypes({ + f0: new Map([[true, false], [false, true]]), // map<bool, bool> + f1: new Map([[0, 0], [1, 127], [-1, -128]]), // map<int8, int8> + f2: new Map([[0, 0], [1, 127], [2, 255]]), // map<uint8, uint8> + f3: new Map([[0, 0], [1, 32767], [2, -32768]]), // map<int16, int16> + f4: new Map([[0, 0], [1, 32768], [2, 0xFFFF]]), // map<uint16, uint16> + f5: new Map([[0, 0], [1, 32767], [2, -32768]]), // map<int32, int32> + f6: new Map([[0, 0], [1, 32768], [2, 0xFFFF]]), // map<uint32, uint32> + f7: new Map([[0, 0], [1, 32767], [2, -32768]]), // map<int64, int64> + f8: new Map([[0, 0], [1, 32768], [2, 0xFFFF]]), // map<uint64, uint64> + f9: new Map([[1000.5, -50000], [100.5, 5000]]), // map<float, float> + f10: new Map([[-100.5, -50000], [0, 50000000]]), // map<double, double> + f11: new Map([["one", "two"], ["free", "four"]]), // map<string, string> + }); + var decodedStruct = structEncodeDecode(mapFieldsStruct); + expect(decodedStruct.f0).toEqual(mapFieldsStruct.f0); + expect(decodedStruct.f1).toEqual(mapFieldsStruct.f1); + expect(decodedStruct.f2).toEqual(mapFieldsStruct.f2); + expect(decodedStruct.f3).toEqual(mapFieldsStruct.f3); + expect(decodedStruct.f4).toEqual(mapFieldsStruct.f4); + expect(decodedStruct.f5).toEqual(mapFieldsStruct.f5); + expect(decodedStruct.f6).toEqual(mapFieldsStruct.f6); + expect(decodedStruct.f7).toEqual(mapFieldsStruct.f7); + expect(decodedStruct.f8).toEqual(mapFieldsStruct.f8); + expect(decodedStruct.f9).toEqual(mapFieldsStruct.f9); + expect(decodedStruct.f10).toEqual(mapFieldsStruct.f10); + expect(decodedStruct.f11).toEqual(mapFieldsStruct.f11); + } + + function testMapValueTypes() { + var mapFieldsStruct = new testStructs.MapValueTypes({ + // map<string, array<string>> + f0: new Map([["a", ["b", "c"]], ["d", ["e"]]]), + // map<string, array<string>?> + f1: new Map([["a", null], ["b", ["c", "d"]]]), + // map<string, array<string?>> + f2: new Map([["a", [null]], ["b", [null, "d"]]]), + // map<string, array<string,2>> + f3: new Map([["a", ["1", "2"]], ["b", ["1", "2"]]]), + // map<string, array<array<string, 2>?>> + f4: new Map([["a", [["1", "2"]]], ["b", [null]]]), + // map<string, array<array<string, 2>, 1>> + f5: new Map([["a", [["1", "2"]]]]), + // map<string, Rect?> + f6: new Map([["a", null]]), + // map<string, map<string, string>> + f7: new Map([["a", new Map([["b", "c"]])]]), + // map<string, array<map<string, string>>> + f8: new Map([["a", [new Map([["b", "c"]])]]]), + // map<string, handle> + f9: new Map([["a", 1234]]), + // map<string, array<handle>> + f10: new Map([["a", [1234, 5678]]]), + // map<string, map<string, handle>> + f11: new Map([["a", new Map([["b", 1234]])]]), + }); + var decodedStruct = structEncodeDecode(mapFieldsStruct); + expect(decodedStruct.f0).toEqual(mapFieldsStruct.f0); + expect(decodedStruct.f1).toEqual(mapFieldsStruct.f1); + expect(decodedStruct.f2).toEqual(mapFieldsStruct.f2); + expect(decodedStruct.f3).toEqual(mapFieldsStruct.f3); + expect(decodedStruct.f4).toEqual(mapFieldsStruct.f4); + expect(decodedStruct.f5).toEqual(mapFieldsStruct.f5); + expect(decodedStruct.f6).toEqual(mapFieldsStruct.f6); + expect(decodedStruct.f7).toEqual(mapFieldsStruct.f7); + expect(decodedStruct.f8).toEqual(mapFieldsStruct.f8); + expect(decodedStruct.f9).toEqual(mapFieldsStruct.f9); + expect(decodedStruct.f10).toEqual(mapFieldsStruct.f10); + expect(decodedStruct.f11).toEqual(mapFieldsStruct.f11); + } + + function testFloatNumberValues() { + var decodedStruct = structEncodeDecode(new testStructs.FloatNumberValues); + expect(decodedStruct.f0).toEqual(testStructs.FloatNumberValues.V0); + expect(decodedStruct.f1).toEqual(testStructs.FloatNumberValues.V1); + expect(decodedStruct.f2).toEqual(testStructs.FloatNumberValues.V2); + expect(decodedStruct.f3).toEqual(testStructs.FloatNumberValues.V3); + expect(decodedStruct.f4).toEqual(testStructs.FloatNumberValues.V4); + expect(decodedStruct.f5).toEqual(testStructs.FloatNumberValues.V5); + expect(decodedStruct.f6).toEqual(testStructs.FloatNumberValues.V6); + expect(decodedStruct.f7).toEqual(testStructs.FloatNumberValues.V7); + expect(decodedStruct.f8).toEqual(testStructs.FloatNumberValues.V8); + expect(decodedStruct.f9).toEqual(testStructs.FloatNumberValues.V9); + } + + function testIntegerNumberValues() { + var decodedStruct = structEncodeDecode(new testStructs.IntegerNumberValues); + expect(decodedStruct.f0).toEqual(testStructs.IntegerNumberValues.V0); + expect(decodedStruct.f1).toEqual(testStructs.IntegerNumberValues.V1); + expect(decodedStruct.f2).toEqual(testStructs.IntegerNumberValues.V2); + expect(decodedStruct.f3).toEqual(testStructs.IntegerNumberValues.V3); + expect(decodedStruct.f4).toEqual(testStructs.IntegerNumberValues.V4); + expect(decodedStruct.f5).toEqual(testStructs.IntegerNumberValues.V5); + expect(decodedStruct.f6).toEqual(testStructs.IntegerNumberValues.V6); + expect(decodedStruct.f7).toEqual(testStructs.IntegerNumberValues.V7); + expect(decodedStruct.f8).toEqual(testStructs.IntegerNumberValues.V8); + expect(decodedStruct.f9).toEqual(testStructs.IntegerNumberValues.V9); + expect(decodedStruct.f10).toEqual(testStructs.IntegerNumberValues.V10); + expect(decodedStruct.f11).toEqual(testStructs.IntegerNumberValues.V11); + expect(decodedStruct.f12).toEqual(testStructs.IntegerNumberValues.V12); + expect(decodedStruct.f13).toEqual(testStructs.IntegerNumberValues.V13); + expect(decodedStruct.f14).toEqual(testStructs.IntegerNumberValues.V14); + expect(decodedStruct.f15).toEqual(testStructs.IntegerNumberValues.V15); + expect(decodedStruct.f16).toEqual(testStructs.IntegerNumberValues.V16); + expect(decodedStruct.f17).toEqual(testStructs.IntegerNumberValues.V17); + expect(decodedStruct.f18).toEqual(testStructs.IntegerNumberValues.V18); + expect(decodedStruct.f19).toEqual(testStructs.IntegerNumberValues.V19); + } + + function testUnsignedNumberValues() { + var decodedStruct = + structEncodeDecode(new testStructs.UnsignedNumberValues); + expect(decodedStruct.f0).toEqual(testStructs.UnsignedNumberValues.V0); + expect(decodedStruct.f1).toEqual(testStructs.UnsignedNumberValues.V1); + expect(decodedStruct.f2).toEqual(testStructs.UnsignedNumberValues.V2); + expect(decodedStruct.f3).toEqual(testStructs.UnsignedNumberValues.V3); + expect(decodedStruct.f4).toEqual(testStructs.UnsignedNumberValues.V4); + expect(decodedStruct.f5).toEqual(testStructs.UnsignedNumberValues.V5); + expect(decodedStruct.f6).toEqual(testStructs.UnsignedNumberValues.V6); + expect(decodedStruct.f7).toEqual(testStructs.UnsignedNumberValues.V7); + expect(decodedStruct.f8).toEqual(testStructs.UnsignedNumberValues.V8); + expect(decodedStruct.f9).toEqual(testStructs.UnsignedNumberValues.V9); + expect(decodedStruct.f10).toEqual(testStructs.UnsignedNumberValues.V10); + expect(decodedStruct.f11).toEqual(testStructs.UnsignedNumberValues.V11); + } + + + function testBitArrayValues() { + var bitArraysStruct = new testStructs.BitArrayValues({ + // array<bool, 1> f0; + f0: [true], + // array<bool, 7> f1; + f1: [true, false, true, false, true, false, true], + // array<bool, 9> f2; + f2: [true, false, true, false, true, false, true, false, true], + // array<bool> f3; + f3: [true, false, true, false, true, false, true, false], + // array<array<bool>> f4; + f4: [[true], [false], [true, false], [true, false, true, false]], + // array<array<bool>?> f5; + f5: [[true], null, null, [true, false, true, false]], + // array<array<bool, 2>?> f6; + f6: [[true, false], [true, false], [true, false]], + }); + var decodedStruct = structEncodeDecode(bitArraysStruct); + expect(decodedStruct.f0).toEqual(bitArraysStruct.f0); + expect(decodedStruct.f1).toEqual(bitArraysStruct.f1); + expect(decodedStruct.f2).toEqual(bitArraysStruct.f2); + expect(decodedStruct.f3).toEqual(bitArraysStruct.f3); + expect(decodedStruct.f4).toEqual(bitArraysStruct.f4); + expect(decodedStruct.f5).toEqual(bitArraysStruct.f5); + expect(decodedStruct.f6).toEqual(bitArraysStruct.f6); + } + + testConstructors(); + testNoDefaultFieldValues(); + testDefaultFieldValues(); + testScopedConstants(); + testMapKeyTypes(); + testMapValueTypes(); + testFloatNumberValues(); + testIntegerNumberValues(); + testUnsignedNumberValues(); + testBitArrayValues(); + this.result = "PASS"; +}); diff --git a/mojo/public/js/support.js b/mojo/public/js/support.js new file mode 100644 index 0000000..025da6c --- /dev/null +++ b/mojo/public/js/support.js @@ -0,0 +1,30 @@ +// 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. + +// Module "mojo/public/js/support" +// +// Note: This file is for documentation purposes only. The code here is not +// actually executed. The real module is implemented natively in Mojo. + +while (1); + +/* + * Waits on the given handle until the state indicated by |signals| is + * satisfied. + * + * @param {MojoHandle} handle The handle to wait on. + * @param {MojoHandleSignals} signals Specifies the condition to wait for. + * @param {function (mojoResult)} callback Called with the result the wait is + * complete. See MojoWait for possible result codes. + * + * @return {MojoWaitId} A waitId that can be passed to cancelWait to cancel the + * wait. + */ +function asyncWait(handle, signals, callback) { [native code] } + +/* + * Cancels the asyncWait operation specified by the given |waitId|. + * @param {MojoWaitId} waitId The waitId returned by asyncWait. + */ +function cancelWait(waitId) { [native code] } diff --git a/mojo/public/js/test/validation_test_input_parser.js b/mojo/public/js/test/validation_test_input_parser.js new file mode 100644 index 0000000..f5a57f9 --- /dev/null +++ b/mojo/public/js/test/validation_test_input_parser.js @@ -0,0 +1,299 @@ +// 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. + +// Support for parsing binary sequences encoded as readable strings +// or ".data" files. The input format is described here: +// mojo/public/cpp/bindings/tests/validation_test_input_parser.h + +define([ + "mojo/public/js/buffer" + ], function(buffer) { + + // Files and Lines represent the raw text from an input string + // or ".data" file. + + function InputError(message, line) { + this.message = message; + this.line = line; + } + + InputError.prototype.toString = function() { + var s = 'Error: ' + this.message; + if (this.line) + s += ', at line ' + + (this.line.number + 1) + ': "' + this.line.contents + '"'; + return s; + } + + function File(contents) { + this.contents = contents; + this.index = 0; + this.lineNumber = 0; + } + + File.prototype.endReached = function() { + return this.index >= this.contents.length; + } + + File.prototype.nextLine = function() { + if (this.endReached()) + return null; + var start = this.index; + var end = this.contents.indexOf('\n', start); + if (end == -1) + end = this.contents.length; + this.index = end + 1; + return new Line(this.contents.substring(start, end), this.lineNumber++); + } + + function Line(contents, number) { + var i = contents.indexOf('//'); + var s = (i == -1) ? contents.trim() : contents.substring(0, i).trim(); + this.contents = contents; + this.items = (s.length > 0) ? s.split(/\s+/) : []; + this.index = 0; + this.number = number; + } + + Line.prototype.endReached = function() { + return this.index >= this.items.length; + } + + var ITEM_TYPE_SIZES = { + u1: 1, u2: 2, u4: 4, u8: 8, s1: 1, s2: 2, s4: 4, s8: 8, b: 1, f: 4, d: 8, + dist4: 4, dist8: 8, anchr: 0, handles: 0 + }; + + function isValidItemType(type) { + return ITEM_TYPE_SIZES[type] !== undefined; + } + + Line.prototype.nextItem = function() { + if (this.endReached()) + return null; + + var itemString = this.items[this.index++]; + var type = 'u1'; + var value = itemString; + + if (itemString.charAt(0) == '[') { + var i = itemString.indexOf(']'); + if (i != -1 && i + 1 < itemString.length) { + type = itemString.substring(1, i); + value = itemString.substring(i + 1); + } else { + throw new InputError('invalid item', this); + } + } + if (!isValidItemType(type)) + throw new InputError('invalid item type', this); + + return new Item(this, type, value); + } + + // The text for each whitespace delimited binary data "item" is represented + // by an Item. + + function Item(line, type, value) { + this.line = line; + this.type = type; + this.value = value; + this.size = ITEM_TYPE_SIZES[type]; + } + + Item.prototype.isFloat = function() { + return this.type == 'f' || this.type == 'd'; + } + + Item.prototype.isInteger = function() { + return ['u1', 'u2', 'u4', 'u8', + 's1', 's2', 's4', 's8'].indexOf(this.type) != -1; + } + + Item.prototype.isNumber = function() { + return this.isFloat() || this.isInteger(); + } + + Item.prototype.isByte = function() { + return this.type == 'b'; + } + + Item.prototype.isDistance = function() { + return this.type == 'dist4' || this.type == 'dist8'; + } + + Item.prototype.isAnchor = function() { + return this.type == 'anchr'; + } + + Item.prototype.isHandles = function() { + return this.type == 'handles'; + } + + // A TestMessage represents the complete binary message loaded from an input + // string or ".data" file. The parseTestMessage() function below constructs + // a TestMessage from a File. + + function TestMessage(byteLength) { + this.index = 0; + this.buffer = new buffer.Buffer(byteLength); + this.distances = {}; + this.handleCount = 0; + } + + function checkItemNumberValue(item, n, min, max) { + if (n < min || n > max) + throw new InputError('invalid item value', item.line); + } + + TestMessage.prototype.addNumber = function(item) { + var n = item.isInteger() ? parseInt(item.value) : parseFloat(item.value); + if (Number.isNaN(n)) + throw new InputError("can't parse item value", item.line); + + switch(item.type) { + case 'u1': + checkItemNumberValue(item, n, 0, 0xFF); + this.buffer.setUint8(this.index, n); + break; + case 'u2': + checkItemNumberValue(item, n, 0, 0xFFFF); + this.buffer.setUint16(this.index, n); + break; + case 'u4': + checkItemNumberValue(item, n, 0, 0xFFFFFFFF); + this.buffer.setUint32(this.index, n); + break; + case 'u8': + checkItemNumberValue(item, n, 0, Number.MAX_SAFE_INTEGER); + this.buffer.setUint64(this.index, n); + break; + case 's1': + checkItemNumberValue(item, n, -128, 127); + this.buffer.setInt8(this.index, n); + break; + case 's2': + checkItemNumberValue(item, n, -32768, 32767); + this.buffer.setInt16(this.index, n); + break; + case 's4': + checkItemNumberValue(item, n, -2147483648, 2147483647); + this.buffer.setInt32(this.index, n); + break; + case 's8': + checkItemNumberValue(item, n, + Number.MIN_SAFE_INTEGER, + Number.MAX_SAFE_INTEGER); + this.buffer.setInt64(this.index, n); + break; + case 'f': + this.buffer.setFloat32(this.index, n); + break; + case 'd': + this.buffer.setFloat64(this.index, n); + break; + + default: + throw new InputError('unrecognized item type', item.line); + } + } + + TestMessage.prototype.addByte = function(item) { + if (!/^[01]{8}$/.test(item.value)) + throw new InputError('invalid byte item value', item.line); + function b(i) { + return (item.value.charAt(7 - i) == '1') ? 1 << i : 0; + } + var n = b(0) | b(1) | b(2) | b(3) | b(4) | b(5) | b(6) | b(7); + this.buffer.setUint8(this.index, n); + } + + TestMessage.prototype.addDistance = function(item) { + if (this.distances[item.value]) + throw new InputError('duplicate distance item', item.line); + this.distances[item.value] = {index: this.index, item: item}; + } + + TestMessage.prototype.addAnchor = function(item) { + var dist = this.distances[item.value]; + if (!dist) + throw new InputError('unmatched anchor item', item.line); + delete this.distances[item.value]; + + var n = this.index - dist.index; + // TODO(hansmuller): validate n + + if (dist.item.type == 'dist4') + this.buffer.setUint32(dist.index, n); + else if (dist.item.type == 'dist8') + this.buffer.setUint64(dist.index, n); + else + throw new InputError('unrecognzed distance item type', dist.item.line); + } + + TestMessage.prototype.addHandles = function(item) { + this.handleCount = parseInt(item.value); + if (Number.isNaN(this.handleCount)) + throw new InputError("can't parse handleCount", item.line); + } + + TestMessage.prototype.addItem = function(item) { + if (item.isNumber()) + this.addNumber(item); + else if (item.isByte()) + this.addByte(item); + else if (item.isDistance()) + this.addDistance(item); + else if (item.isAnchor()) + this.addAnchor(item); + else if (item.isHandles()) + this.addHandles(item); + else + throw new InputError('unrecognized item type', item.line); + + this.index += item.size; + } + + TestMessage.prototype.unanchoredDistances = function() { + var names = null; + for (var name in this.distances) { + if (this.distances.hasOwnProperty(name)) + names = (names === null) ? name : names + ' ' + name; + } + return names; + } + + function parseTestMessage(text) { + var file = new File(text); + var items = []; + var messageLength = 0; + while(!file.endReached()) { + var line = file.nextLine(); + while (!line.endReached()) { + var item = line.nextItem(); + if (item.isHandles() && items.length > 0) + throw new InputError('handles item is not first'); + messageLength += item.size; + items.push(item); + } + } + + var msg = new TestMessage(messageLength); + for (var i = 0; i < items.length; i++) + msg.addItem(items[i]); + + if (messageLength != msg.index) + throw new InputError('failed to compute message length'); + var names = msg.unanchoredDistances(); + if (names) + throw new InputError('no anchors for ' + names, 0); + + return msg; + } + + var exports = {}; + exports.parseTestMessage = parseTestMessage; + exports.InputError = InputError; + return exports; +}); diff --git a/mojo/public/js/threading.js b/mojo/public/js/threading.js new file mode 100644 index 0000000..cfe5037 --- /dev/null +++ b/mojo/public/js/threading.js @@ -0,0 +1,21 @@ +// 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. + +// Module "mojo/public/js/threading" +// +// 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 +// 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. + +while (1); + +/** + * Quits the current message loop, esssentially: + * base::MessageLoop::current()->QuitNow(); +*/ +function quit() { [native code] } diff --git a/mojo/public/js/unicode.js b/mojo/public/js/unicode.js new file mode 100644 index 0000000..be2ba0e --- /dev/null +++ b/mojo/public/js/unicode.js @@ -0,0 +1,51 @@ +// 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. + +/** + * Defines functions for translating between JavaScript strings and UTF8 strings + * stored in ArrayBuffers. There is much room for optimization in this code if + * it proves necessary. + */ +define("mojo/public/js/unicode", function() { + /** + * Decodes the UTF8 string from the given buffer. + * @param {ArrayBufferView} buffer The buffer containing UTF8 string data. + * @return {string} The corresponding JavaScript string. + */ + function decodeUtf8String(buffer) { + return decodeURIComponent(escape(String.fromCharCode.apply(null, buffer))); + } + + /** + * Encodes the given JavaScript string into UTF8. + * @param {string} str The string to encode. + * @param {ArrayBufferView} outputBuffer The buffer to contain the result. + * Should be pre-allocated to hold enough space. Use |utf8Length| to determine + * how much space is required. + * @return {number} The number of bytes written to |outputBuffer|. + */ + function encodeUtf8String(str, outputBuffer) { + var utf8String = unescape(encodeURIComponent(str)); + if (outputBuffer.length < utf8String.length) + throw new Error("Buffer too small for encodeUtf8String"); + for (var i = 0; i < outputBuffer.length && i < utf8String.length; i++) + outputBuffer[i] = utf8String.charCodeAt(i); + return i; + } + + /** + * Returns the number of bytes that a UTF8 encoding of the JavaScript string + * |str| would occupy. + */ + function utf8Length(str) { + var utf8String = unescape(encodeURIComponent(str)); + return utf8String.length; + } + + var exports = {}; + exports.decodeUtf8String = decodeUtf8String; + exports.encodeUtf8String = encodeUtf8String; + exports.utf8Length = utf8Length; + return exports; +}); diff --git a/mojo/public/js/union_unittests.js b/mojo/public/js/union_unittests.js new file mode 100644 index 0000000..5dcda7d --- /dev/null +++ b/mojo/public/js/union_unittests.js @@ -0,0 +1,184 @@ +// Copyright 2015 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. + +define([ + "gin/test/expect", + "mojo/public/interfaces/bindings/tests/test_unions.mojom", + "mojo/public/js/codec", + "mojo/public/js/validator", +], function(expect, + unions, + codec, + validator) { + function testConstructors() { + var u = new unions.PodUnion(); + expect(u.$data).toEqual(null); + expect(u.$tag).toBeUndefined(); + + u.f_uint32 = 32; + + expect(u.f_uint32).toEqual(32); + expect(u.$tag).toEqual(unions.PodUnion.Tags.f_uint32); + + var u = new unions.PodUnion({f_uint64: 64}); + expect(u.f_uint64).toEqual(64); + expect(u.$tag).toEqual(unions.PodUnion.Tags.f_uint64); + expect(function() {var v = u.f_uint32;}).toThrow(); + + expect(function() { + var u = new unions.PodUnion({ + f_uint64: 64, + f_uint32: 32, + }); + }).toThrow(); + + expect(function() { + var u = new unions.PodUnion({ foo: 64 }); }).toThrow(); + + expect(function() { + var u = new unions.PodUnion([1,2,3,4]); }).toThrow(); + } + + function structEncodeDecode(struct) { + var structClass = struct.constructor; + var builder = new codec.MessageBuilder(1234, structClass.encodedSize); + builder.encodeStruct(structClass, struct); + + var message = builder.finish(); + + var messageValidator = new validator.Validator(message); + var err = structClass.validate(messageValidator, codec.kMessageHeaderSize); + expect(err).toEqual(validator.validationError.NONE); + + var reader = new codec.MessageReader(message); + var view = reader.decoder.buffer.dataView; + + return reader.decodeStruct(structClass); + } + + function testBasicEncoding() { + var s = new unions.WrapperStruct({ + pod_union: new unions.PodUnion({ + f_uint64: 64})}); + + var decoded = structEncodeDecode(s); + expect(decoded).toEqual(s); + + var s = new unions.WrapperStruct({ + object_union: new unions.ObjectUnion({ + f_dummy: new unions.DummyStruct({ + f_int8: 8})})}); + + var decoded = structEncodeDecode(s); + expect(decoded).toEqual(s); + + var s = new unions.WrapperStruct({ + object_union: new unions.ObjectUnion({ + f_array_int8: [1, 2, 3]})}); + + var decoded = structEncodeDecode(s); + expect(decoded).toEqual(s); + + var s = new unions.WrapperStruct({ + object_union: new unions.ObjectUnion({ + f_map_int8: new Map([ + ["first", 1], + ["second", 2], + ])})}); + + var decoded = structEncodeDecode(s); + expect(decoded).toEqual(s); + + // Encoding a union with no member set is an error. + var s = new unions.WrapperStruct({ + object_union: new unions.ObjectUnion()}); + expect(function() { + structEncodeDecode(s); }).toThrow(); + } + + function testUnionsInArrayEncoding() { + var s = new unions.SmallStruct({ + pod_union_array: [ + new unions.PodUnion({f_uint32: 32}), + new unions.PodUnion({f_uint64: 64}), + ] + }); + + var decoded = structEncodeDecode(s); + expect(decoded).toEqual(s); + } + + function testUnionsInMapEncoding() { + var s = new unions.SmallStruct({ + pod_union_map: new Map([ + ["thirty-two", new unions.PodUnion({f_uint32: 32})], + ["sixty-four", new unions.PodUnion({f_uint64: 64})], + ]) + }); + + var decoded = structEncodeDecode(s); + expect(decoded).toEqual(s); + } + + function testNestedUnionsEncoding() { + var s = new unions.WrapperStruct({ + object_union: new unions.ObjectUnion({ + f_pod_union: new unions.PodUnion({f_uint32: 32}) + })}); + var decoded = structEncodeDecode(s); + expect(decoded).toEqual(s); + } + + function structValidate(struct) { + var structClass = struct.constructor; + var builder = new codec.MessageBuilder(1234, structClass.encodedSize); + builder.encodeStruct(structClass, struct); + + var message = builder.finish(); + + var messageValidator = new validator.Validator(message); + return structClass.validate(messageValidator, codec.kMessageHeaderSize); + } + + function testNullUnionMemberValidation() { + var s = new unions.WrapperStruct({ + object_union: new unions.ObjectUnion({ + f_dummy: null})}); + + var err = structValidate(s); + expect(err).toEqual(validator.validationError.UNEXPECTED_NULL_POINTER); + + var s = new unions.WrapperStruct({ + object_union: new unions.ObjectUnion({ + f_nullable: null})}); + + var err = structValidate(s); + expect(err).toEqual(validator.validationError.NONE); + } + + function testNullUnionValidation() { + var s = new unions.SmallStructNonNullableUnion({ + pod_union: null}); + + var err = structValidate(s); + expect(err).toEqual(validator.validationError.UNEXPECTED_NULL_UNION); + + var s = new unions.WrapperStruct({ + object_union: new unions.ObjectUnion({ + f_pod_union: null}) + }); + + var err = structValidate(s); + expect(err).toEqual(validator.validationError.UNEXPECTED_NULL_UNION); + } + + testConstructors(); + testBasicEncoding(); + testUnionsInArrayEncoding(); + testUnionsInMapEncoding(); + testNestedUnionsEncoding(); + testNullUnionMemberValidation(); + testNullUnionValidation(); + this.result = "PASS"; +}); diff --git a/mojo/public/js/validation_unittests.js b/mojo/public/js/validation_unittests.js new file mode 100644 index 0000000..4fdb5d4 --- /dev/null +++ b/mojo/public/js/validation_unittests.js @@ -0,0 +1,316 @@ +// 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. + +define([ + "console", + "file", + "gin/test/expect", + "mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom", + "mojo/public/js/buffer", + "mojo/public/js/codec", + "mojo/public/js/connection", + "mojo/public/js/connector", + "mojo/public/js/core", + "mojo/public/js/test/validation_test_input_parser", + "mojo/public/js/router", + "mojo/public/js/validator", +], function(console, + file, + expect, + testInterface, + buffer, + codec, + connection, + connector, + core, + parser, + router, + validator) { + + var noError = validator.validationError.NONE; + + function checkTestMessageParser() { + function TestMessageParserFailure(message, input) { + this.message = message; + this.input = input; + } + + TestMessageParserFailure.prototype.toString = function() { + return 'Error: ' + this.message + ' for "' + this.input + '"'; + } + + function checkData(data, expectedData, input) { + if (data.byteLength != expectedData.byteLength) { + var s = "message length (" + data.byteLength + ") doesn't match " + + "expected length: " + expectedData.byteLength; + throw new TestMessageParserFailure(s, input); + } + + for (var i = 0; i < data.byteLength; i++) { + if (data.getUint8(i) != expectedData.getUint8(i)) { + var s = 'message data mismatch at byte offset ' + i; + throw new TestMessageParserFailure(s, input); + } + } + } + + function testFloatItems() { + var input = '[f]+.3e9 [d]-10.03'; + var msg = parser.parseTestMessage(input); + var expectedData = new buffer.Buffer(12); + expectedData.setFloat32(0, +.3e9); + expectedData.setFloat64(4, -10.03); + checkData(msg.buffer, expectedData, input); + } + + function testUnsignedIntegerItems() { + var input = '[u1]0x10// hello world !! \n\r \t [u2]65535 \n' + + '[u4]65536 [u8]0xFFFFFFFFFFFFF 0 0Xff'; + var msg = parser.parseTestMessage(input); + var expectedData = new buffer.Buffer(17); + expectedData.setUint8(0, 0x10); + expectedData.setUint16(1, 65535); + expectedData.setUint32(3, 65536); + expectedData.setUint64(7, 0xFFFFFFFFFFFFF); + expectedData.setUint8(15, 0); + expectedData.setUint8(16, 0xff); + checkData(msg.buffer, expectedData, input); + } + + function testSignedIntegerItems() { + var input = '[s8]-0x800 [s1]-128\t[s2]+0 [s4]-40'; + var msg = parser.parseTestMessage(input); + var expectedData = new buffer.Buffer(15); + expectedData.setInt64(0, -0x800); + expectedData.setInt8(8, -128); + expectedData.setInt16(9, 0); + expectedData.setInt32(11, -40); + checkData(msg.buffer, expectedData, input); + } + + function testByteItems() { + var input = '[b]00001011 [b]10000000 // hello world\n [b]00000000'; + var msg = parser.parseTestMessage(input); + var expectedData = new buffer.Buffer(3); + expectedData.setUint8(0, 11); + expectedData.setUint8(1, 128); + expectedData.setUint8(2, 0); + checkData(msg.buffer, expectedData, input); + } + + function testAnchors() { + var input = '[dist4]foo 0 [dist8]bar 0 [anchr]foo [anchr]bar'; + var msg = parser.parseTestMessage(input); + var expectedData = new buffer.Buffer(14); + expectedData.setUint32(0, 14); + expectedData.setUint8(4, 0); + expectedData.setUint64(5, 9); + expectedData.setUint8(13, 0); + checkData(msg.buffer, expectedData, input); + } + + function testHandles() { + var input = '// This message has handles! \n[handles]50 [u8]2'; + var msg = parser.parseTestMessage(input); + var expectedData = new buffer.Buffer(8); + expectedData.setUint64(0, 2); + + if (msg.handleCount != 50) { + var s = 'wrong handle count (' + msg.handleCount + ')'; + throw new TestMessageParserFailure(s, input); + } + checkData(msg.buffer, expectedData, input); + } + + function testEmptyInput() { + var msg = parser.parseTestMessage(''); + if (msg.buffer.byteLength != 0) + throw new TestMessageParserFailure('expected empty message', ''); + } + + function testBlankInput() { + var input = ' \t // hello world \n\r \t// the answer is 42 '; + var msg = parser.parseTestMessage(input); + if (msg.buffer.byteLength != 0) + throw new TestMessageParserFailure('expected empty message', input); + } + + function testInvalidInput() { + function parserShouldFail(input) { + try { + parser.parseTestMessage(input); + } catch (e) { + if (e instanceof parser.InputError) + return; + throw new TestMessageParserFailure( + 'unexpected exception ' + e.toString(), input); + } + throw new TestMessageParserFailure("didn't detect invalid input", file); + } + + ['/ hello world', + '[u1]x', + '[u2]-1000', + '[u1]0x100', + '[s2]-0x8001', + '[b]1', + '[b]1111111k', + '[dist4]unmatched', + '[anchr]hello [dist8]hello', + '[dist4]a [dist4]a [anchr]a', + // '[dist4]a [anchr]a [dist4]a [anchr]a', + '0 [handles]50' + ].forEach(parserShouldFail); + } + + try { + testFloatItems(); + testUnsignedIntegerItems(); + testSignedIntegerItems(); + testByteItems(); + testInvalidInput(); + testEmptyInput(); + testBlankInput(); + testHandles(); + testAnchors(); + } catch (e) { + return e.toString(); + } + return null; + } + + function getMessageTestFiles(key) { + var sourceRoot = file.getSourceRootDirectory(); + expect(sourceRoot).not.toBeNull(); + + var testDir = sourceRoot + + "/mojo/public/interfaces/bindings/tests/data/validation/"; + var testFiles = file.getFilesInDirectory(testDir); + expect(testFiles).not.toBeNull(); + expect(testFiles.length).toBeGreaterThan(0); + + // The matching ".data" pathnames with the extension removed. + return testFiles.filter(function(s) { + return s.substr(-5) == ".data"; + }).map(function(s) { + return testDir + s.slice(0, -5); + }).filter(function(s) { + return s.indexOf(key) != -1; + }); + } + + function readTestMessage(filename) { + var contents = file.readFileToString(filename + ".data"); + expect(contents).not.toBeNull(); + return parser.parseTestMessage(contents); + } + + function readTestExpected(filename) { + var contents = file.readFileToString(filename + ".expected"); + expect(contents).not.toBeNull(); + return contents.trim(); + } + + function checkValidationResult(testFile, err) { + var actualResult = (err === noError) ? "PASS" : err; + var expectedResult = readTestExpected(testFile); + if (actualResult != expectedResult) + console.log("[Test message validation failed: " + testFile + " ]"); + expect(actualResult).toEqual(expectedResult); + } + + function testMessageValidation(key, filters) { + var testFiles = getMessageTestFiles(key); + expect(testFiles.length).toBeGreaterThan(0); + + 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 + // cpp to js. + if (testFiles[i].indexOf("overflow") != -1 || + testFiles[i].indexOf("mthd11") != -1 || + testFiles[i].indexOf("no_such_method") != -1 || + testFiles[i].indexOf("invalid_request_flags") != -1 || + testFiles[i].indexOf("invalid_response_flags") != -1) { + console.log("[Skipping " + testFiles[i] + "]"); + continue; + } + + var testMessage = readTestMessage(testFiles[i]); + var handles = new Array(testMessage.handleCount); + var message = new codec.Message(testMessage.buffer, handles); + var messageValidator = new validator.Validator(message); + + var err = messageValidator.validateMessageHeader(); + for (var j = 0; err === noError && j < filters.length; ++j) + err = filters[j](messageValidator); + + checkValidationResult(testFiles[i], err); + } + } + + function testConformanceMessageValidation() { + testMessageValidation("conformance_", [ + testInterface.ConformanceTestInterface.validateRequest]); + } + + function testIntegratedMessageValidation(testFilesPattern) { + var testFiles = getMessageTestFiles(testFilesPattern); + expect(testFiles.length).toBeGreaterThan(0); + + for (var i = 0; i < testFiles.length; i++) { + var testMessage = readTestMessage(testFiles[i]); + var handles = new Array(testMessage.handleCount); + var testMessagePipe = new core.createMessagePipe(); + expect(testMessagePipe.result).toBe(core.RESULT_OK); + + var writeMessageValue = core.writeMessage( + testMessagePipe.handle0, + new Uint8Array(testMessage.buffer.arrayBuffer), + new Array(testMessage.handleCount), + core.WRITE_MESSAGE_FLAG_NONE); + expect(writeMessageValue).toBe(core.RESULT_OK); + + var testConnection = new connection.TestConnection( + testMessagePipe.handle1, + testInterface.IntegrationTestInterface.stubClass, + testInterface.IntegrationTestInterface.proxyClass); + + var validationError = noError; + testConnection.router_.validationErrorHandler = function(err) { + validationError = err; + } + + testConnection.router_.connector_.deliverMessage(); + checkValidationResult(testFiles[i], validationError); + + testConnection.close(); + expect(core.close(testMessagePipe.handle0)).toBe(core.RESULT_OK); + } + } + + function testIntegratedMessageHeaderValidation() { + testIntegratedMessageValidation("integration_msghdr"); + } + + function testIntegratedRequestMessageValidation() { + testIntegratedMessageValidation("integration_intf_rqst"); + } + + function testIntegratedResponseMessageValidation() { + testIntegratedMessageValidation("integration_intf_resp"); + } + + expect(checkTestMessageParser()).toBeNull(); + testConformanceMessageValidation(); + testIntegratedMessageHeaderValidation(); + testIntegratedResponseMessageValidation(); + testIntegratedRequestMessageValidation(); + this.result = "PASS"; +}); diff --git a/mojo/public/js/validator.js b/mojo/public/js/validator.js new file mode 100644 index 0000000..cbf7521 --- /dev/null +++ b/mojo/public/js/validator.js @@ -0,0 +1,415 @@ +// 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. + +define("mojo/public/js/validator", [ + "mojo/public/js/codec", +], function(codec) { + + var validationError = { + NONE: 'VALIDATION_ERROR_NONE', + MISALIGNED_OBJECT: 'VALIDATION_ERROR_MISALIGNED_OBJECT', + ILLEGAL_MEMORY_RANGE: 'VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE', + UNEXPECTED_STRUCT_HEADER: 'VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER', + UNEXPECTED_ARRAY_HEADER: 'VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER', + ILLEGAL_HANDLE: 'VALIDATION_ERROR_ILLEGAL_HANDLE', + UNEXPECTED_INVALID_HANDLE: 'VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE', + ILLEGAL_POINTER: 'VALIDATION_ERROR_ILLEGAL_POINTER', + UNEXPECTED_NULL_POINTER: 'VALIDATION_ERROR_UNEXPECTED_NULL_POINTER', + MESSAGE_HEADER_INVALID_FLAGS: + 'VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS', + MESSAGE_HEADER_MISSING_REQUEST_ID: + 'VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID', + DIFFERENT_SIZED_ARRAYS_IN_MAP: + 'VALIDATION_ERROR_DIFFERENT_SIZED_ARRAYS_IN_MAP', + INVALID_UNION_SIZE: 'VALIDATION_ERROR_INVALID_UNION_SIZE', + UNEXPECTED_NULL_UNION: 'VALIDATION_ERROR_UNEXPECTED_NULL_UNION', + }; + + var NULL_MOJO_POINTER = "NULL_MOJO_POINTER"; + + function isStringClass(cls) { + return cls === codec.String || cls === codec.NullableString; + } + + function isHandleClass(cls) { + return cls === codec.Handle || cls === codec.NullableHandle; + } + + function isInterfaceClass(cls) { + return cls === codec.Interface || cls === codec.NullableInterface; + } + + function isNullable(type) { + return type === codec.NullableString || type === codec.NullableHandle || + type === codec.NullableInterface || + type instanceof codec.NullableArrayOf || + type instanceof codec.NullablePointerTo; + } + + function Validator(message) { + this.message = message; + this.offset = 0; + this.handleIndex = 0; + } + + Object.defineProperty(Validator.prototype, "offsetLimit", { + get: function() { return this.message.buffer.byteLength; } + }); + + Object.defineProperty(Validator.prototype, "handleIndexLimit", { + get: function() { return this.message.handles.length; } + }); + + // True if we can safely allocate a block of bytes from start to + // to start + numBytes. + Validator.prototype.isValidRange = function(start, numBytes) { + // Only positive JavaScript integers that are less than 2^53 + // (Number.MAX_SAFE_INTEGER) can be represented exactly. + if (start < this.offset || numBytes <= 0 || + !Number.isSafeInteger(start) || + !Number.isSafeInteger(numBytes)) + return false; + + var newOffset = start + numBytes; + if (!Number.isSafeInteger(newOffset) || newOffset > this.offsetLimit) + return false; + + return true; + } + + Validator.prototype.claimRange = function(start, numBytes) { + if (this.isValidRange(start, numBytes)) { + this.offset = start + numBytes; + return true; + } + return false; + } + + Validator.prototype.claimHandle = function(index) { + if (index === codec.kEncodedInvalidHandleValue) + return true; + + if (index < this.handleIndex || index >= this.handleIndexLimit) + return false; + + // This is safe because handle indices are uint32. + this.handleIndex = index + 1; + return true; + } + + Validator.prototype.validateHandle = function(offset, nullable) { + var index = this.message.buffer.getUint32(offset); + + if (index === codec.kEncodedInvalidHandleValue) + return nullable ? + validationError.NONE : validationError.UNEXPECTED_INVALID_HANDLE; + + 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) { + if (!codec.isAligned(offset)) + return validationError.MISALIGNED_OBJECT; + + if (!this.isValidRange(offset, codec.kStructHeaderSize)) + 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) + return validationError.UNEXPECTED_STRUCT_HEADER; + + if (!this.claimRange(offset, numBytes)) + return validationError.ILLEGAL_MEMORY_RANGE; + + return validationError.NONE; + } + + Validator.prototype.validateMessageHeader = function() { + var err = this.validateStructHeader(0, codec.kMessageHeaderSize, 0); + if (err != validationError.NONE) + return err; + + var numBytes = this.message.getHeaderNumBytes(); + var version = this.message.getHeaderVersion(); + + var validVersionAndNumBytes = + (version == 0 && numBytes == codec.kMessageHeaderSize) || + (version == 1 && + numBytes == codec.kMessageWithRequestIDHeaderSize) || + (version > 1 && + numBytes >= codec.kMessageWithRequestIDHeaderSize); + if (!validVersionAndNumBytes) + return validationError.UNEXPECTED_STRUCT_HEADER; + + var expectsResponse = this.message.expectsResponse(); + var isResponse = this.message.isResponse(); + + if (version == 0 && (expectsResponse || isResponse)) + return validationError.MESSAGE_HEADER_MISSING_REQUEST_ID; + + if (isResponse && expectsResponse) + 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 + // pointer's value is not valid. + Validator.prototype.decodePointer = function(offset) { + var pointerValue = this.message.buffer.getUint64(offset); + if (pointerValue === 0) + 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); + }; + + Validator.prototype.decodeUnionTag = function(offset) { + return this.message.buffer.getUint32(offset + 4); + }; + + Validator.prototype.validateArrayPointer = function( + offset, elementSize, elementType, nullable, expectedDimensionSizes, + currentDimension) { + var arrayOffset = this.decodePointer(offset); + if (arrayOffset === null) + return validationError.ILLEGAL_POINTER; + + if (arrayOffset === NULL_MOJO_POINTER) + return nullable ? + validationError.NONE : validationError.UNEXPECTED_NULL_POINTER; + + return this.validateArray(arrayOffset, elementSize, elementType, + expectedDimensionSizes, currentDimension); + } + + Validator.prototype.validateStructPointer = function( + offset, structClass, nullable) { + var structOffset = this.decodePointer(offset); + if (structOffset === null) + return validationError.ILLEGAL_POINTER; + + if (structOffset === NULL_MOJO_POINTER) + return nullable ? + validationError.NONE : validationError.UNEXPECTED_NULL_POINTER; + + return structClass.validate(this, structOffset); + } + + Validator.prototype.validateUnion = function( + offset, unionClass, nullable) { + var size = this.message.buffer.getUint32(offset); + if (size == 0) { + return nullable ? + validationError.NONE : validationError.UNEXPECTED_NULL_UNION; + } + + return unionClass.validate(this, offset); + } + + Validator.prototype.validateNestedUnion = function( + offset, unionClass, nullable) { + var unionOffset = this.decodePointer(offset); + if (unionOffset === null) + return validationError.ILLEGAL_POINTER; + + if (unionOffset === NULL_MOJO_POINTER) + return nullable ? + validationError.NONE : validationError.UNEXPECTED_NULL_UNION; + + return this.validateUnion(unionOffset, unionClass, nullable); + } + + // This method assumes that the array at arrayPointerOffset has + // been validated. + + 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) { + // Validate the implicit map struct: + // struct {array<keyClass> keys; array<valueClass> values}; + var structOffset = this.decodePointer(offset); + if (structOffset === null) + return validationError.ILLEGAL_POINTER; + + if (structOffset === NULL_MOJO_POINTER) + return mapIsNullable ? + validationError.NONE : validationError.UNEXPECTED_NULL_POINTER; + + var mapEncodedSize = codec.kStructHeaderSize + codec.kMapStructPayloadSize; + var err = this.validateStructHeader(structOffset, mapEncodedSize, 0); + if (err !== validationError.NONE) + return err; + + // Validate the keys array. + var keysArrayPointerOffset = structOffset + codec.kStructHeaderSize; + err = this.validateArrayPointer( + keysArrayPointerOffset, keyClass.encodedSize, keyClass, false, [0], 0); + if (err !== validationError.NONE) + return err; + + // Validate the values array. + var valuesArrayPointerOffset = keysArrayPointerOffset + 8; + var valuesArrayDimensions = [0]; // Validate the actual length below. + if (valueClass instanceof codec.ArrayOf) + valuesArrayDimensions = + valuesArrayDimensions.concat(valueClass.dimensions()); + var err = this.validateArrayPointer(valuesArrayPointerOffset, + valueClass.encodedSize, + valueClass, + valueIsNullable, + valuesArrayDimensions, + 0); + if (err !== validationError.NONE) + return err; + + // Validate the lengths of the keys and values arrays. + var keysArrayLength = this.arrayLength(keysArrayPointerOffset); + var valuesArrayLength = this.arrayLength(valuesArrayPointerOffset); + if (keysArrayLength != valuesArrayLength) + 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 + + Validator.prototype.validateArray = + function (offset, elementSize, elementType, expectedDimensionSizes, + currentDimension) { + if (!codec.isAligned(offset)) + return validationError.MISALIGNED_OBJECT; + + if (!this.isValidRange(offset, codec.kArrayHeaderSize)) + return validationError.ILLEGAL_MEMORY_RANGE; + + var numBytes = this.message.buffer.getUint32(offset); + var numElements = this.message.buffer.getUint32(offset + 4); + + // Note: this computation is "safe" because elementSize <= 8 and + // numElements is a uint32. + var elementsTotalSize = (elementType === codec.PackedBool) ? + Math.ceil(numElements / 8) : (elementSize * numElements); + + if (numBytes < codec.kArrayHeaderSize + elementsTotalSize) + return validationError.UNEXPECTED_ARRAY_HEADER; + + if (expectedDimensionSizes[currentDimension] != 0 && + numElements != expectedDimensionSizes[currentDimension]) { + return validationError.UNEXPECTED_ARRAY_HEADER; + } + + if (!this.claimRange(offset, numBytes)) + return validationError.ILLEGAL_MEMORY_RANGE; + + // Validate the array's elements if they are pointers or handles. + + var elementsOffset = offset + codec.kArrayHeaderSize; + var nullable = isNullable(elementType); + + if (isHandleClass(elementType)) + return this.validateHandleElements(elementsOffset, numElements, nullable); + if (isInterfaceClass(elementType)) + return this.validateInterfaceElements( + elementsOffset, numElements, nullable); + if (isStringClass(elementType)) + return this.validateArrayElements( + elementsOffset, numElements, codec.Uint8, nullable, [0], 0); + if (elementType instanceof codec.PointerTo) + return this.validateStructElements( + elementsOffset, numElements, elementType.cls, nullable); + if (elementType instanceof codec.ArrayOf) + return this.validateArrayElements( + elementsOffset, numElements, elementType.cls, nullable, + expectedDimensionSizes, currentDimension + 1); + + return validationError.NONE; + } + + // Note: the |offset + i * elementSize| computation in the validateFooElements + // methods below is "safe" because elementSize <= 8, offset and + // numElements are uint32, and 0 <= i < numElements. + + Validator.prototype.validateHandleElements = + function(offset, numElements, nullable) { + var elementSize = codec.Handle.encodedSize; + for (var i = 0; i < numElements; i++) { + var elementOffset = offset + i * elementSize; + var err = this.validateHandle(elementOffset, nullable); + if (err != validationError.NONE) + return err; + } + return validationError.NONE; + } + + Validator.prototype.validateInterfaceElements = + function(offset, numElements, nullable) { + var elementSize = codec.Interface.encodedSize; + for (var i = 0; i < numElements; i++) { + var elementOffset = offset + i * elementSize; + var err = this.validateInterface(elementOffset, nullable); + if (err != validationError.NONE) + return err; + } + return validationError.NONE; + } + + // The elementClass parameter is the element type of the element arrays. + Validator.prototype.validateArrayElements = + function(offset, numElements, elementClass, nullable, + expectedDimensionSizes, currentDimension) { + var elementSize = codec.PointerTo.prototype.encodedSize; + for (var i = 0; i < numElements; i++) { + var elementOffset = offset + i * elementSize; + var err = this.validateArrayPointer( + elementOffset, elementClass.encodedSize, elementClass, nullable, + expectedDimensionSizes, currentDimension); + if (err != validationError.NONE) + return err; + } + return validationError.NONE; + } + + Validator.prototype.validateStructElements = + function(offset, numElements, structClass, nullable) { + var elementSize = codec.PointerTo.prototype.encodedSize; + for (var i = 0; i < numElements; i++) { + var elementOffset = offset + i * elementSize; + var err = + this.validateStructPointer(elementOffset, structClass, nullable); + if (err != validationError.NONE) + return err; + } + return validationError.NONE; + } + + var exports = {}; + exports.validationError = validationError; + exports.Validator = Validator; + return exports; +}); diff --git a/mojo/public/mojo.gni b/mojo/public/mojo.gni deleted file mode 100644 index d7bf744..0000000 --- a/mojo/public/mojo.gni +++ /dev/null @@ -1,14 +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. - -# TODO(dpranke): Remove all of these variables. - -mojo_sdk_root = "//third_party/mojo/src" -mojo_network_service_root = "//mojo/services" -mojo_use_prebuilt_mojo_shell = false -mojo_use_prebuilt_network_service = false -mojo_use_dart_apptest_framework = false -mojo_use_application_in_sdk = false -mojo_use_network_in_sdk = false -mojo_root = get_path_info("../../third_party/mojo/src/", "abspath") diff --git a/mojo/public/mojo_application.gni b/mojo/public/mojo_application.gni index 1609b02..72398b7 100644 --- a/mojo/public/mojo_application.gni +++ b/mojo/public/mojo_application.gni @@ -3,7 +3,6 @@ # found in the LICENSE file. import("//build/toolchain/toolchain.gni") -import("mojo.gni") # Generate a binary Mojo application in a self-named directory. # Application resources are copied to a "resources" directory alongside the app. @@ -83,24 +82,11 @@ template("mojo_native_application") { data_deps += invoker.data_deps } - # Copy any necessary prebuilt artifacts. - if (mojo_use_prebuilt_mojo_shell) { - data_deps += - [ rebase_path("mojo/public/tools:copy_mojo_shell", ".", mojo_root) ] - } - if (mojo_use_prebuilt_network_service) { - data_deps += [ rebase_path("mojo/public/tools:copy_network_service", - ".", - mojo_root) ] - } - - deps = rebase_path([ - "mojo/public/gles2:for_shared_library", - "mojo/public/c/system:for_shared_library", - ], - ".", - mojo_root) - deps += [ "//mojo/platform_handle:for_shared_library" ] + deps = [ + "//mojo/platform_handle:for_shared_library", + "//mojo/public/gles2:for_shared_library", + "//mojo/public/c/system:for_shared_library", + ] deps += mojo_deps if (defined(invoker.public_deps)) { @@ -200,23 +186,11 @@ template("mojo_native_application") { data_deps = invoker.data_deps } - # Copy any necessary prebuilt artifacts. - if (mojo_use_prebuilt_mojo_shell) { - data_deps += - [ rebase_path("mojo/public/tools:copy_mojo_shell", ".", mojo_root) ] - } - if (mojo_use_prebuilt_network_service) { - data_deps += [ rebase_path("mojo/public/tools:copy_network_service", - ".", - mojo_root) ] - } + deps = [ + "//mojo/public/c/system", + "//mojo/public/platform/nacl:system", + ] - deps = rebase_path([ - "mojo/public/c/system", - "mojo/public/platform/nacl:system", - ], - ".", - mojo_root) deps += mojo_deps if (defined(invoker.public_deps)) { public_deps = invoker.public_deps @@ -260,7 +234,7 @@ template("mojo_native_application") { visibility = invoker.visibility } - script = rebase_path("mojo/public/tools/prepend.py", ".", mojo_root) + script = "//mojo/public/tools/prepend.py" input_path = "${root_out_dir}/${nexe_name}" inputs = [ @@ -361,7 +335,7 @@ if (is_android) { } action(target_name) { - script = rebase_path("mojo/public/tools/prepend.py", ".", mojo_root) + script = "//mojo/public/tools/prepend.py" base_target_name = target_name if (defined(invoker.output_name)) { diff --git a/mojo/public/mojo_sdk.gni b/mojo/public/mojo_sdk.gni new file mode 100644 index 0000000..1c5d9d3 --- /dev/null +++ b/mojo/public/mojo_sdk.gni @@ -0,0 +1,153 @@ +# 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. + +# Takes as input a "source_set" that includes dependencies that are relative to +# the parent directory of the Mojo public SDK (given in |mojo_sdk_deps|). +# Generates a source_set that has the mojo_sdk_deps added as ordinary deps +# rebased to the current directory. +# By default, restricts the entries that are given in invoker.deps and +# invoker.public_deps to be only within the same file and on a small set of +# whitelisted external dependencies. This check can be elided by setting +# restrict_external_deps to false in the invoker. DO NOT DO THIS in +# //mojo/public. +# +# Example of a mojo_sdk_source_set: +# +# mojo_sdk_source_set("foo") { +# sources = [ +# "foo.h", +# "foo.cc", +# ] +# +# # Same-file deps are specified in the ordinary way. Any external +# dependencies are specified the same way (although in general there should +# be very few of these). +# deps = [ +# ":bar", +# ] +# +# # Mojo SDK deps are specified relative to the containing directory of the +# SDK via mojo_sdk_deps. +# mojo_sdk_deps = [ +# "mojo/public/cpp/bindings", +# "mojo/public/cpp/environment", +# "mojo/public/cpp/system", +# ] +# } +# +template("mojo_sdk_source_set") { + source_set(target_name) { + if (defined(invoker.visibility)) { + visibility = invoker.visibility + } else { + visibility = [ "*" ] + } + if (defined(invoker.mojo_edk_visibility)) { + foreach(edk_target, invoker.mojo_edk_visibility) { + # Check that the EDK target was not mistakenly given as an absolute + # path. + assert(get_path_info(edk_target, "abspath") != edk_target) + visibility += [ rebase_path(edk_target, ".", "//third_party/mojo/src") ] + } + } + + if (defined(invoker.testonly)) { + testonly = invoker.testonly + } + + if (defined(invoker.sources)) { + sources = invoker.sources + } + + if (defined(invoker.defines)) { + defines = invoker.defines + } + + if (defined(invoker.libs)) { + libs = invoker.libs + } + + public_configs = [] + if (defined(invoker.public_configs)) { + public_configs += invoker.public_configs + } + + if (defined(invoker.configs)) { + configs += invoker.configs + } + + if (defined(invoker.allow_circular_includes_from)) { + allow_circular_includes_from = invoker.allow_circular_includes_from + } + + if (defined(invoker.public_deps) || defined(invoker.deps)) { + restrict_external_deps = true + if (defined(invoker.restrict_external_deps)) { + restrict_external_deps = invoker.restrict_external_deps + } + } + + public_deps = [] + if (defined(invoker.public_deps)) { + foreach(dep, invoker.public_deps) { + if (restrict_external_deps) { + # The only deps that are not specified relative to the location of + # the Mojo SDK should be on targets within the same file or on a + # whitelisted set of external dependencies. + assert(get_path_info(dep, "dir") == ".") + } + public_deps += [ dep ] + } + } + if (defined(invoker.mojo_edk_public_deps)) { + foreach(edk_dep, invoker.mojo_edk_public_deps) { + # Check that the EDK dep was not mistakenly given as an absolute path. + assert(get_path_info(edk_dep, "abspath") != edk_dep) + public_deps += [ rebase_path(edk_dep, ".", "//third_party/mojo/src") ] + } + } + if (defined(invoker.mojo_sdk_public_deps)) { + foreach(sdk_dep, invoker.mojo_sdk_public_deps) { + # Check that the SDK dep was not mistakenly given as an absolute path. + assert(get_path_info(sdk_dep, "abspath") != sdk_dep) + public_deps += [ rebase_path(sdk_dep, ".", "//") ] + } + } + + deps = [] + if (defined(invoker.deps)) { + foreach(dep, invoker.deps) { + if (restrict_external_deps) { + # The only deps that are not specified relative to the location of + # the Mojo SDK should be on targets within the same file or on a + # whitelisted set of external dependencies. + dep_dir = get_path_info(dep, "dir") + assert(dep_dir == "." || dep == "//testing/gtest" || + dep == "//mojo/environment:chromium" || + dep == "//mojo/message_pump") + } + deps += [ dep ] + } + } + if (defined(invoker.mojo_edk_deps)) { + foreach(edk_dep, invoker.mojo_edk_deps) { + # Check that the EDK dep was not mistakenly given as an absolute path. + assert(get_path_info(edk_dep, "abspath") != edk_dep) + deps += [ rebase_path(edk_dep, ".", "//third_party/mojo/src") ] + } + } + if (defined(invoker.mojo_sdk_deps)) { + foreach(sdk_dep, invoker.mojo_sdk_deps) { + # Check that the SDK dep was not mistakenly given as an absolute path. + assert(get_path_info(sdk_dep, "abspath") != sdk_dep) + deps += [ rebase_path(sdk_dep, ".", "//") ] + } + } + + data_deps = [] + if (defined(invoker.data_deps)) { + data_deps = invoker.data_deps + } + } +} diff --git a/mojo/public/platform/nacl/BUILD.gn b/mojo/public/platform/nacl/BUILD.gn new file mode 100644 index 0000000..17edf32 --- /dev/null +++ b/mojo/public/platform/nacl/BUILD.gn @@ -0,0 +1,31 @@ +# 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. + +assert(is_nacl) + +import("../../mojo_sdk.gni") + +# Untrusted code +if (is_nacl) { + # Thunk mapping the Mojo public API onto NaCl syscalls. + mojo_sdk_source_set("mojo") { + sources = [ + "libmojo.cc", + "mojo_irt.h", + ] + mojo_sdk_deps = [ "mojo/public/c/system" ] + } + + mojo_sdk_source_set("system") { + sources = [ + "mojo_initial_handle.h", + "mojo_main_thunk.cc", + ] + + mojo_sdk_deps = [ + "mojo/public/c/system", + "mojo/public/platform/nacl:mojo", + ] + } +} diff --git a/mojo/public/platform/nacl/DEPS b/mojo/public/platform/nacl/DEPS new file mode 100644 index 0000000..ef97f1a --- /dev/null +++ b/mojo/public/platform/nacl/DEPS @@ -0,0 +1,4 @@ +include_rules = [ + # This code is checked into the chromium repo so it's fine to depend on this. + "+native_client", +] diff --git a/mojo/public/platform/nacl/libmojo.cc b/mojo/public/platform/nacl/libmojo.cc new file mode 100644 index 0000000..de0b495 --- /dev/null +++ b/mojo/public/platform/nacl/libmojo.cc @@ -0,0 +1,227 @@ +// 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. + +// WARNING this file was generated by generate_nacl_bindings.py +// Do not edit by hand. + +#include "mojo/public/c/system/core.h" +#include "mojo/public/platform/nacl/mojo_irt.h" +#include "native_client/src/public/chrome_main.h" +#include "native_client/src/public/imc_syscalls.h" +#include "native_client/src/public/imc_types.h" +#include "native_client/src/untrusted/irt/irt.h" + +// The value for this FD must not conflict with uses inside Chromium. However, +// mojo/nacl doesn't depend on any Chromium headers, so we can't use a #define +// from there. +#define NACL_MOJO_DESC (NACL_CHROME_DESC_BASE + 3) + +bool g_irt_mojo_valid = false; +struct nacl_irt_mojo g_irt_mojo; + +struct nacl_irt_mojo* get_irt_mojo() { + if (!g_irt_mojo_valid) { + size_t rc = nacl_interface_query(NACL_IRT_MOJO_v0_1, &g_irt_mojo, + sizeof(g_irt_mojo)); + if (rc != sizeof(g_irt_mojo)) + return NULL; + else + g_irt_mojo_valid = true; + } + return &g_irt_mojo; +} + +MojoResult MojoCreateSharedBuffer( + const struct MojoCreateSharedBufferOptions* options, + uint64_t num_bytes, + MojoHandle* shared_buffer_handle) { + struct nacl_irt_mojo* irt_mojo = get_irt_mojo(); + if (irt_mojo == NULL) + return MOJO_RESULT_INTERNAL; + return irt_mojo->MojoCreateSharedBuffer(options, num_bytes, + shared_buffer_handle); +} + +MojoResult MojoDuplicateBufferHandle( + MojoHandle buffer_handle, + const struct MojoDuplicateBufferHandleOptions* options, + MojoHandle* new_buffer_handle) { + struct nacl_irt_mojo* irt_mojo = get_irt_mojo(); + if (irt_mojo == NULL) + return MOJO_RESULT_INTERNAL; + return irt_mojo->MojoDuplicateBufferHandle(buffer_handle, options, + new_buffer_handle); +} + +MojoResult MojoMapBuffer(MojoHandle buffer_handle, + uint64_t offset, + uint64_t num_bytes, + void** buffer, + MojoMapBufferFlags flags) { + struct nacl_irt_mojo* irt_mojo = get_irt_mojo(); + if (irt_mojo == NULL) + return MOJO_RESULT_INTERNAL; + return irt_mojo->MojoMapBuffer(buffer_handle, offset, num_bytes, buffer, + flags); +} + +MojoResult MojoUnmapBuffer(void* buffer) { + struct nacl_irt_mojo* irt_mojo = get_irt_mojo(); + if (irt_mojo == NULL) + return MOJO_RESULT_INTERNAL; + return irt_mojo->MojoUnmapBuffer(buffer); +} + +MojoResult MojoCreateDataPipe(const struct MojoCreateDataPipeOptions* options, + MojoHandle* data_pipe_producer_handle, + MojoHandle* data_pipe_consumer_handle) { + struct nacl_irt_mojo* irt_mojo = get_irt_mojo(); + if (irt_mojo == NULL) + return MOJO_RESULT_INTERNAL; + return irt_mojo->MojoCreateDataPipe(options, data_pipe_producer_handle, + data_pipe_consumer_handle); +} + +MojoResult MojoWriteData(MojoHandle data_pipe_producer_handle, + const void* elements, + uint32_t* num_bytes, + MojoWriteDataFlags flags) { + struct nacl_irt_mojo* irt_mojo = get_irt_mojo(); + if (irt_mojo == NULL) + return MOJO_RESULT_INTERNAL; + return irt_mojo->MojoWriteData(data_pipe_producer_handle, elements, num_bytes, + flags); +} + +MojoResult MojoBeginWriteData(MojoHandle data_pipe_producer_handle, + void** buffer, + uint32_t* buffer_num_bytes, + MojoWriteDataFlags flags) { + struct nacl_irt_mojo* irt_mojo = get_irt_mojo(); + if (irt_mojo == NULL) + return MOJO_RESULT_INTERNAL; + return irt_mojo->MojoBeginWriteData(data_pipe_producer_handle, buffer, + buffer_num_bytes, flags); +} + +MojoResult MojoEndWriteData(MojoHandle data_pipe_producer_handle, + uint32_t num_bytes_written) { + struct nacl_irt_mojo* irt_mojo = get_irt_mojo(); + if (irt_mojo == NULL) + return MOJO_RESULT_INTERNAL; + return irt_mojo->MojoEndWriteData(data_pipe_producer_handle, + num_bytes_written); +} + +MojoResult MojoReadData(MojoHandle data_pipe_consumer_handle, + void* elements, + uint32_t* num_bytes, + MojoReadDataFlags flags) { + struct nacl_irt_mojo* irt_mojo = get_irt_mojo(); + if (irt_mojo == NULL) + return MOJO_RESULT_INTERNAL; + return irt_mojo->MojoReadData(data_pipe_consumer_handle, elements, num_bytes, + flags); +} + +MojoResult MojoBeginReadData(MojoHandle data_pipe_consumer_handle, + const void** buffer, + uint32_t* buffer_num_bytes, + MojoReadDataFlags flags) { + struct nacl_irt_mojo* irt_mojo = get_irt_mojo(); + if (irt_mojo == NULL) + return MOJO_RESULT_INTERNAL; + return irt_mojo->MojoBeginReadData(data_pipe_consumer_handle, buffer, + buffer_num_bytes, flags); +} + +MojoResult MojoEndReadData(MojoHandle data_pipe_consumer_handle, + uint32_t num_bytes_read) { + struct nacl_irt_mojo* irt_mojo = get_irt_mojo(); + if (irt_mojo == NULL) + return MOJO_RESULT_INTERNAL; + return irt_mojo->MojoEndReadData(data_pipe_consumer_handle, num_bytes_read); +} + +MojoTimeTicks MojoGetTimeTicksNow() { + struct nacl_irt_mojo* irt_mojo = get_irt_mojo(); + if (irt_mojo == NULL) + return MOJO_RESULT_INTERNAL; + return irt_mojo->MojoGetTimeTicksNow(); +} + +MojoResult MojoClose(MojoHandle handle) { + struct nacl_irt_mojo* irt_mojo = get_irt_mojo(); + if (irt_mojo == NULL) + return MOJO_RESULT_INTERNAL; + return irt_mojo->MojoClose(handle); +} + +MojoResult MojoWait(MojoHandle handle, + MojoHandleSignals signals, + MojoDeadline deadline, + struct MojoHandleSignalsState* signals_state) { + struct nacl_irt_mojo* irt_mojo = get_irt_mojo(); + if (irt_mojo == NULL) + return MOJO_RESULT_INTERNAL; + return irt_mojo->MojoWait(handle, signals, deadline, signals_state); +} + +MojoResult MojoWaitMany(const MojoHandle* handles, + const MojoHandleSignals* signals, + uint32_t num_handles, + MojoDeadline deadline, + uint32_t* result_index, + struct MojoHandleSignalsState* signals_states) { + struct nacl_irt_mojo* irt_mojo = get_irt_mojo(); + if (irt_mojo == NULL) + return MOJO_RESULT_INTERNAL; + return irt_mojo->MojoWaitMany(handles, signals, num_handles, deadline, + result_index, signals_states); +} + +MojoResult MojoCreateMessagePipe( + const struct MojoCreateMessagePipeOptions* options, + MojoHandle* message_pipe_handle0, + MojoHandle* message_pipe_handle1) { + struct nacl_irt_mojo* irt_mojo = get_irt_mojo(); + if (irt_mojo == NULL) + return MOJO_RESULT_INTERNAL; + return irt_mojo->MojoCreateMessagePipe(options, message_pipe_handle0, + message_pipe_handle1); +} + +MojoResult MojoWriteMessage(MojoHandle message_pipe_handle, + const void* bytes, + uint32_t num_bytes, + const MojoHandle* handles, + uint32_t num_handles, + MojoWriteMessageFlags flags) { + struct nacl_irt_mojo* irt_mojo = get_irt_mojo(); + if (irt_mojo == NULL) + return MOJO_RESULT_INTERNAL; + return irt_mojo->MojoWriteMessage(message_pipe_handle, bytes, num_bytes, + handles, num_handles, flags); +} + +MojoResult MojoReadMessage(MojoHandle message_pipe_handle, + void* bytes, + uint32_t* num_bytes, + MojoHandle* handles, + uint32_t* num_handles, + MojoReadMessageFlags flags) { + struct nacl_irt_mojo* irt_mojo = get_irt_mojo(); + if (irt_mojo == NULL) + return MOJO_RESULT_INTERNAL; + return irt_mojo->MojoReadMessage(message_pipe_handle, bytes, num_bytes, + handles, num_handles, flags); +} + +MojoResult _MojoGetInitialHandle(MojoHandle* handle) { + struct nacl_irt_mojo* irt_mojo = get_irt_mojo(); + if (irt_mojo == NULL) + return MOJO_RESULT_INTERNAL; + return irt_mojo->_MojoGetInitialHandle(handle); +} + diff --git a/mojo/public/platform/nacl/mojo_initial_handle.h b/mojo/public/platform/nacl/mojo_initial_handle.h new file mode 100644 index 0000000..e02146c --- /dev/null +++ b/mojo/public/platform/nacl/mojo_initial_handle.h @@ -0,0 +1,14 @@ +// 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. + +#ifndef MOJO_PUBLIC_PLATFORM_NACL_MOJO_INITIAL_HANDLE_H_ +#define MOJO_PUBLIC_PLATFORM_NACL_MOJO_INITIAL_HANDLE_H_ + +#include "mojo/public/c/system/types.h" + +// Provides a MojoHandle that allows untrusted code to communicate with Mojo +// interfaces outside the sandbox or in other processes. +MojoResult _MojoGetInitialHandle(MojoHandle* out_handle); + +#endif // MOJO_PUBLIC_PLATFORM_NACL_MOJO_INITIAL_HANDLE_H_ diff --git a/mojo/public/platform/nacl/mojo_irt.h b/mojo/public/platform/nacl/mojo_irt.h new file mode 100644 index 0000000..db3a180 --- /dev/null +++ b/mojo/public/platform/nacl/mojo_irt.h @@ -0,0 +1,100 @@ +// Copyright 2015 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. + +// WARNING this file was generated by generate_nacl_bindings.py +// Do not edit by hand. + +#ifndef MOJO_PUBLIC_PLATFORM_NACL_MOJO_IRT_H_ +#define MOJO_PUBLIC_PLATFORM_NACL_MOJO_IRT_H_ + +#include "mojo/public/c/system/buffer.h" +#include "mojo/public/c/system/data_pipe.h" +#include "mojo/public/c/system/message_pipe.h" +#include "mojo/public/c/system/types.h" + +#define NACL_IRT_MOJO_v0_1 "nacl-irt-mojo-0.1" + +struct nacl_irt_mojo { + MojoResult (*MojoCreateSharedBuffer)( + const struct MojoCreateSharedBufferOptions* options, + uint64_t num_bytes, + MojoHandle* shared_buffer_handle); + MojoResult (*MojoDuplicateBufferHandle)( + MojoHandle buffer_handle, + const struct MojoDuplicateBufferHandleOptions* options, + MojoHandle* new_buffer_handle); + MojoResult (*MojoMapBuffer)(MojoHandle buffer_handle, + uint64_t offset, + uint64_t num_bytes, + void** buffer, + MojoMapBufferFlags flags); + MojoResult (*MojoUnmapBuffer)(void* buffer); + MojoResult (*MojoCreateDataPipe)( + const struct MojoCreateDataPipeOptions* options, + MojoHandle* data_pipe_producer_handle, + MojoHandle* data_pipe_consumer_handle); + MojoResult (*MojoWriteData)(MojoHandle data_pipe_producer_handle, + const void* elements, + uint32_t* num_bytes, + MojoWriteDataFlags flags); + MojoResult (*MojoBeginWriteData)(MojoHandle data_pipe_producer_handle, + void** buffer, + uint32_t* buffer_num_bytes, + MojoWriteDataFlags flags); + MojoResult (*MojoEndWriteData)(MojoHandle data_pipe_producer_handle, + uint32_t num_bytes_written); + MojoResult (*MojoReadData)(MojoHandle data_pipe_consumer_handle, + void* elements, + uint32_t* num_bytes, + MojoReadDataFlags flags); + MojoResult (*MojoBeginReadData)(MojoHandle data_pipe_consumer_handle, + const void** buffer, + uint32_t* buffer_num_bytes, + MojoReadDataFlags flags); + MojoResult (*MojoEndReadData)(MojoHandle data_pipe_consumer_handle, + uint32_t num_bytes_read); + MojoTimeTicks (*MojoGetTimeTicksNow)(); + MojoResult (*MojoClose)(MojoHandle handle); + MojoResult (*MojoWait)(MojoHandle handle, + MojoHandleSignals signals, + MojoDeadline deadline, + struct MojoHandleSignalsState* signals_state); + MojoResult (*MojoWaitMany)(const MojoHandle* handles, + const MojoHandleSignals* signals, + uint32_t num_handles, + MojoDeadline deadline, + uint32_t* result_index, + struct MojoHandleSignalsState* signals_states); + MojoResult (*MojoCreateMessagePipe)( + const struct MojoCreateMessagePipeOptions* options, + MojoHandle* message_pipe_handle0, + MojoHandle* message_pipe_handle1); + MojoResult (*MojoWriteMessage)(MojoHandle message_pipe_handle, + const void* bytes, + uint32_t num_bytes, + const MojoHandle* handles, + uint32_t num_handles, + MojoWriteMessageFlags flags); + MojoResult (*MojoReadMessage)(MojoHandle message_pipe_handle, + void* bytes, + uint32_t* num_bytes, + MojoHandle* handles, + uint32_t* num_handles, + MojoReadMessageFlags flags); + MojoResult (*_MojoGetInitialHandle)(MojoHandle* handle); +}; + +#ifdef __cplusplus +extern "C" { +#endif + +size_t mojo_irt_query(const char* interface_ident, + void* table, + size_t tablesize); + +#ifdef __cplusplus +} +#endif + +#endif // MOJO_PUBLIC_PLATFORM_NACL_MOJO_IRT_H_ diff --git a/mojo/public/platform/nacl/mojo_main_thunk.cc b/mojo/public/platform/nacl/mojo_main_thunk.cc new file mode 100644 index 0000000..7cb5b52 --- /dev/null +++ b/mojo/public/platform/nacl/mojo_main_thunk.cc @@ -0,0 +1,12 @@ +// Copyright 2015 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. + +#include "mojo/public/c/system/main.h" +#include "mojo/public/platform/nacl/mojo_initial_handle.h" + +int main() { + MojoHandle handle; + _MojoGetInitialHandle(&handle); + return MojoMain(handle); +} diff --git a/mojo/public/platform/native/BUILD.gn b/mojo/public/platform/native/BUILD.gn new file mode 100644 index 0000000..aded499 --- /dev/null +++ b/mojo/public/platform/native/BUILD.gn @@ -0,0 +1,73 @@ +# 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. + +assert(!defined(is_nacl) || !is_nacl) + +import("../../mojo_sdk.gni") + +mojo_sdk_source_set("system") { + sources = [ + "system_thunks.cc", + "system_thunks.h", + ] + defines = [ "MOJO_SYSTEM_IMPLEMENTATION" ] + mojo_sdk_deps = [ "mojo/public/c/system" ] + + # The GYP target analogous to this one builds this code into a + # static library. When building for Android, both the GYP and GN + # builds add --exclude-libs=ALL globally, which means that all + # symbols in static libraries are excluded from export. That's a + # problem, as code outside this target needs to be able to call + # MojoSetSystemThunks(). Therefore, the GYP target needs to specifiy + # that all dependent targets remove that link flag. Since GN uses a + # source_set here, this flag change is not needed. +} + +mojo_sdk_source_set("gles2") { + sources = [ + "gles2_impl_chromium_extension_thunks.cc", + "gles2_impl_chromium_extension_thunks.h", + "gles2_impl_thunks.cc", + "gles2_impl_thunks.h", + "gles2_thunks.cc", + "gles2_thunks.h", + ] + + defines = [ "MOJO_GLES2_IMPLEMENTATION" ] + + configs = [ "//third_party/khronos:khronos_headers" ] + + mojo_sdk_deps = [ + "mojo/public/c/gles2", + "mojo/public/c/environment", + "mojo/public/c/system", + ] + + if (is_mac) { + # TODO(GYP): Make it a run-path dependent library. + # 'DYLIB_INSTALL_NAME_BASE': '@loader_path', + } +} + +mojo_sdk_source_set("gpu_thunks") { + sources = [] +} + +mojo_sdk_source_set("mgl_thunks") { + sources = [ + "mgl_thunks.c", + "mgl_thunks.h", + ] + + mojo_sdk_deps = [ "mojo/public/c/gpu:MGL" ] +} + +mojo_sdk_source_set("mgl_onscreen_thunks") { + sources = [ + "mgl_onscreen_thunks.c", + "mgl_onscreen_thunks.h", + ] + + mojo_sdk_deps = [ "mojo/public/c/gpu:MGL_onscreen" ] +} diff --git a/mojo/public/platform/native/gles2_impl_chromium_extension_thunks.cc b/mojo/public/platform/native/gles2_impl_chromium_extension_thunks.cc new file mode 100644 index 0000000..0296d79 --- /dev/null +++ b/mojo/public/platform/native/gles2_impl_chromium_extension_thunks.cc @@ -0,0 +1,31 @@ +// Copyright 2015 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. + +#include "mojo/public/platform/native/gles2_impl_chromium_extension_thunks.h" + +#include <assert.h> + +#include "mojo/public/platform/native/thunk_export.h" + +extern "C" { +static MojoGLES2ImplChromiumExtensionThunks g_impl_chromium_extension_thunks = {0}; + +#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \ + ReturnType GL_APIENTRY gl##Function PARAMETERS { \ + assert(g_impl_chromium_extension_thunks.Function); \ + return g_impl_chromium_extension_thunks.Function ARGUMENTS; \ + } +#include "mojo/public/c/gles2/gles2_call_visitor_chromium_extension_autogen.h" +#undef VISIT_GL_CALL + +extern "C" THUNK_EXPORT size_t MojoSetGLES2ImplChromiumExtensionThunks( + const MojoGLES2ImplChromiumExtensionThunks* + gles2_impl_chromium_extension_thunks) { + if (gles2_impl_chromium_extension_thunks->size >= + sizeof(g_impl_chromium_extension_thunks)) + g_impl_chromium_extension_thunks = *gles2_impl_chromium_extension_thunks; + return sizeof(g_impl_chromium_extension_thunks); +} + +} // extern "C" diff --git a/mojo/public/platform/native/gles2_impl_chromium_extension_thunks.h b/mojo/public/platform/native/gles2_impl_chromium_extension_thunks.h new file mode 100644 index 0000000..adac8ea --- /dev/null +++ b/mojo/public/platform/native/gles2_impl_chromium_extension_thunks.h @@ -0,0 +1,44 @@ +// Copyright 2015 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_PLATFORM_NATIVE_GLES2_IMPL_CHROMIUM_EXTENSION_THUNKS_H_ +#define MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_CHROMIUM_EXTENSION_THUNKS_H_ + +#include <stddef.h> + +#include "mojo/public/c/gles2/chromium_extension.h" + +// Specifies the API for the GLES2 CHROMIUM extension. +#pragma pack(push, 8) +struct MojoGLES2ImplChromiumExtensionThunks { + size_t size; // Should be set to sizeof(*this). + +#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \ + ReturnType(GL_APIENTRY *Function) PARAMETERS; +#include "mojo/public/c/gles2/gles2_call_visitor_chromium_extension_autogen.h" +#undef VISIT_GL_CALL +}; +#pragma pack(pop) + +// Intended to be called from the embedder to get the embedder's implementation +// of GLES2. +inline MojoGLES2ImplChromiumExtensionThunks +MojoMakeGLES2ImplChromiumExtensionThunks() { + MojoGLES2ImplChromiumExtensionThunks gles2_impl_chromium_extension_thunks = { + sizeof(MojoGLES2ImplChromiumExtensionThunks), +#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) gl##Function, +#include "mojo/public/c/gles2/gles2_call_visitor_chromium_extension_autogen.h" +#undef VISIT_GL_CALL + }; + + return gles2_impl_chromium_extension_thunks; +} + +// Use this type for the function found by dynamically discovering it in +// a DSO linked with mojo_system. +// The contents of |gles2_impl_chromium_extension_thunks| are copied. +typedef size_t (*MojoSetGLES2ImplChromiumExtensionThunksFn)( + const MojoGLES2ImplChromiumExtensionThunks* thunks); + +#endif // MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_CHROMIUM_EXTENSION_THUNKS_H_ diff --git a/mojo/public/platform/native/gles2_impl_thunks.cc b/mojo/public/platform/native/gles2_impl_thunks.cc new file mode 100644 index 0000000..1acf5a4 --- /dev/null +++ b/mojo/public/platform/native/gles2_impl_thunks.cc @@ -0,0 +1,29 @@ +// 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. + +#include "mojo/public/platform/native/gles2_impl_thunks.h" + +#include <assert.h> + +#include "mojo/public/platform/native/thunk_export.h" + +extern "C" { +static MojoGLES2ImplThunks g_impl_thunks = {0}; + +#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \ + ReturnType GL_APIENTRY gl##Function PARAMETERS { \ + assert(g_impl_thunks.Function); \ + return g_impl_thunks.Function ARGUMENTS; \ + } +#include "mojo/public/c/gles2/gles2_call_visitor_autogen.h" +#undef VISIT_GL_CALL + +extern "C" THUNK_EXPORT size_t +MojoSetGLES2ImplThunks(const MojoGLES2ImplThunks* gles2_impl_thunks) { + if (gles2_impl_thunks->size >= sizeof(g_impl_thunks)) + g_impl_thunks = *gles2_impl_thunks; + return sizeof(g_impl_thunks); +} + +} // extern "C" diff --git a/mojo/public/platform/native/gles2_impl_thunks.h b/mojo/public/platform/native/gles2_impl_thunks.h new file mode 100644 index 0000000..47765f2 --- /dev/null +++ b/mojo/public/platform/native/gles2_impl_thunks.h @@ -0,0 +1,49 @@ +// 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. + +#ifndef MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_THUNKS_H_ +#define MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_THUNKS_H_ + +#include <stddef.h> + +#include "mojo/public/c/gles2/gles2.h" + +// Like MojoGLES2ControlThunks, but specifies the frozen GLES2 API. Separated +// out as MojoGLES2ControlThunks may be modified and added to, but this +// interface is frozen. +#pragma pack(push, 8) +struct MojoGLES2ImplThunks { + size_t size; // Should be set to sizeof(MojoGLES2ImplThunks). + +#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \ + ReturnType(GL_APIENTRY *Function) PARAMETERS; +#include "mojo/public/c/gles2/gles2_call_visitor_autogen.h" +#undef VISIT_GL_CALL +}; +#pragma pack(pop) + +// Intended to be called from the embedder to get the embedder's implementation +// of GLES2. +inline MojoGLES2ImplThunks MojoMakeGLES2ImplThunks() { + MojoGLES2ImplThunks gles2_impl_thunks = { + sizeof(MojoGLES2ImplThunks), +#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) gl##Function, +#include "mojo/public/c/gles2/gles2_call_visitor_autogen.h" +#undef VISIT_GL_CALL + }; + + return gles2_impl_thunks; +} + +// Use this type for the function found by dynamically discovering it in +// a DSO linked with mojo_system. For example: +// MojoSetGLES2ImplThunksFn mojo_set_gles2_impl_thunks_fn = +// reinterpret_cast<MojoSetGLES2ImplThunksFn>( +// app_library.GetFunctionPointer("MojoSetGLES2ImplThunks")); +// The expected size of |gles2_impl_thunks| is returned. +// The contents of |gles2_impl_thunks| are copied. +typedef size_t (*MojoSetGLES2ImplThunksFn)( + const MojoGLES2ImplThunks* gles2_impl_thunks); + +#endif // MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_THUNKS_H_ diff --git a/mojo/public/platform/native/gles2_thunks.cc b/mojo/public/platform/native/gles2_thunks.cc new file mode 100644 index 0000000..b51de76 --- /dev/null +++ b/mojo/public/platform/native/gles2_thunks.cc @@ -0,0 +1,60 @@ +// 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. + +#include "mojo/public/platform/native/gles2_thunks.h" + +#include <assert.h> + +#include "mojo/public/platform/native/thunk_export.h" + +extern "C" { + +static MojoGLES2ControlThunks g_control_thunks = {0}; + +MojoGLES2Context MojoGLES2CreateContext(MojoHandle handle, + const int32_t* attrib_list, + MojoGLES2ContextLost lost_callback, + void* closure, + const MojoAsyncWaiter* async_waiter) { + assert(g_control_thunks.GLES2CreateContext); + return g_control_thunks.GLES2CreateContext( + handle, attrib_list, lost_callback, closure, async_waiter); +} + +void MojoGLES2DestroyContext(MojoGLES2Context context) { + assert(g_control_thunks.GLES2DestroyContext); + g_control_thunks.GLES2DestroyContext(context); +} + +void MojoGLES2MakeCurrent(MojoGLES2Context context) { + assert(g_control_thunks.GLES2MakeCurrent); + g_control_thunks.GLES2MakeCurrent(context); +} + +void MojoGLES2SwapBuffers() { + assert(g_control_thunks.GLES2SwapBuffers); + g_control_thunks.GLES2SwapBuffers(); +} + +void* MojoGLES2GetGLES2Interface(MojoGLES2Context context) { + assert(g_control_thunks.GLES2GetGLES2Interface); + return g_control_thunks.GLES2GetGLES2Interface(context); +} + +void MojoGLES2SignalSyncPoint(MojoGLES2Context context, + uint32_t sync_point, + MojoGLES2SignalSyncPointCallback callback, + void* closure) { + assert(g_control_thunks.GLES2SignalSyncPoint); + g_control_thunks.GLES2SignalSyncPoint(context, sync_point, callback, closure); +} + +extern "C" THUNK_EXPORT size_t MojoSetGLES2ControlThunks( + const MojoGLES2ControlThunks* gles2_control_thunks) { + if (gles2_control_thunks->size >= sizeof(g_control_thunks)) + g_control_thunks = *gles2_control_thunks; + return sizeof(g_control_thunks); +} + +} // extern "C" diff --git a/mojo/public/platform/native/gles2_thunks.h b/mojo/public/platform/native/gles2_thunks.h new file mode 100644 index 0000000..c9d30f5 --- /dev/null +++ b/mojo/public/platform/native/gles2_thunks.h @@ -0,0 +1,67 @@ +// 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. + +#ifndef MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_THUNKS_H_ +#define MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_THUNKS_H_ + +#include <stddef.h> + +#include "mojo/public/c/gles2/gles2.h" + +// Structure used to bind the interface which manipulates GLES2 surfaces to a +// DSO to theose of the embedder. +// +// This is the ABI between the embedder and the DSO. It can only have new +// functions added to the end. No other changes are supported. +#pragma pack(push, 8) +struct MojoGLES2ControlThunks { + size_t size; // Should be set to sizeof(MojoGLES2ControlThunks). + + MojoGLES2Context (*GLES2CreateContext)(MojoHandle handle, + const int32_t* attrib_list, + MojoGLES2ContextLost lost_callback, + void* closure, + const MojoAsyncWaiter* async_waiter); + void (*GLES2DestroyContext)(MojoGLES2Context context); + void (*GLES2MakeCurrent)(MojoGLES2Context context); + void (*GLES2SwapBuffers)(); + + // TODO(piman): We shouldn't have to leak this interface, especially in a + // type-unsafe way. + void* (*GLES2GetGLES2Interface)(MojoGLES2Context context); + + void (*GLES2SignalSyncPoint)(MojoGLES2Context context, + uint32_t sync_point, + MojoGLES2SignalSyncPointCallback callback, + void* closure); +}; +#pragma pack(pop) + +// Intended to be called from the embedder. Returns an object initialized to +// contain pointers to each of the embedder's MojoGLES2ControlThunks functions. +inline MojoGLES2ControlThunks MojoMakeGLES2ControlThunks() { + MojoGLES2ControlThunks gles2_control_thunks = { + sizeof(MojoGLES2ControlThunks), + MojoGLES2CreateContext, + MojoGLES2DestroyContext, + MojoGLES2MakeCurrent, + MojoGLES2SwapBuffers, + MojoGLES2GetGLES2Interface, + MojoGLES2SignalSyncPoint, + }; + + return gles2_control_thunks; +} + +// Use this type for the function found by dynamically discovering it in +// a DSO linked with mojo_system. For example: +// MojoSetGLES2ControlThunksFn mojo_set_gles2_control_thunks_fn = +// reinterpret_cast<MojoSetGLES2ControlThunksFn>( +// app_library.GetFunctionPointer("MojoSetGLES2ControlThunks")); +// The expected size of |gles2_control_thunks| is returned. +// The contents of |gles2_control_thunks| are copied. +typedef size_t (*MojoSetGLES2ControlThunksFn)( + const MojoGLES2ControlThunks* gles2_control_thunks); + +#endif // MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_THUNKS_H_ diff --git a/mojo/public/platform/native/mgl_onscreen_thunks.c b/mojo/public/platform/native/mgl_onscreen_thunks.c new file mode 100644 index 0000000..c41b583 --- /dev/null +++ b/mojo/public/platform/native/mgl_onscreen_thunks.c @@ -0,0 +1,28 @@ +// Copyright 2015 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. + +#include "mojo/public/platform/native/mgl_onscreen_thunks.h" + +#include <assert.h> + +#include "mojo/public/platform/native/thunk_export.h" + +static struct MGLOnscreenThunks g_onscreen_thunks = {0}; + +void MGLResizeSurface(uint32_t width, uint32_t height) { + assert(g_onscreen_thunks.MGLResizeSurface); + g_onscreen_thunks.MGLResizeSurface(width, height); +} + +void MGLSwapBuffers(void) { + assert(g_onscreen_thunks.MGLSwapBuffers); + g_onscreen_thunks.MGLSwapBuffers(); +} + +THUNK_EXPORT size_t MojoSetMGLOnscreenThunks( + const struct MGLOnscreenThunks* mgl_onscreen_thunks) { + if (mgl_onscreen_thunks->size >= sizeof(g_onscreen_thunks)) + g_onscreen_thunks = *mgl_onscreen_thunks; + return sizeof(g_onscreen_thunks); +} diff --git a/mojo/public/platform/native/mgl_onscreen_thunks.h b/mojo/public/platform/native/mgl_onscreen_thunks.h new file mode 100644 index 0000000..40dcb80 --- /dev/null +++ b/mojo/public/platform/native/mgl_onscreen_thunks.h @@ -0,0 +1,46 @@ +// Copyright 2015 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_PLATFORM_NATIVE_MGL_ONSCREEN_THUNKS_H_ +#define MOJO_PUBLIC_PLATFORM_NATIVE_MGL_ONSCREEN_THUNKS_H_ + +#include <stddef.h> + +#include "mojo/public/c/gpu/MGL/mgl_onscreen.h" + +// Structure used to bind the interface which manipulates MGL contexts to a +// DSO to theose of the embedder. +// +// This is the ABI between the embedder and the DSO. It can only have new +// functions added to the end. No other changes are supported. +#pragma pack(push, 8) +struct MGLOnscreenThunks { + size_t size; // Should be set to sizeof(MojoMGLOnscreenThunks). + + void (*MGLResizeSurface)(uint32_t width, uint32_t height); + void (*MGLSwapBuffers)(void); +}; +#pragma pack(pop) + +// Intended to be called from the embedder. Returns an object initialized to +// contain pointers to each of the embedder's MGLOnscreenThunks functions. +inline struct MGLOnscreenThunks MojoMakeMGLOnscreenThunks() { + struct MGLOnscreenThunks mgl_onscreen_thunks = { + sizeof(struct MGLOnscreenThunks), MGLResizeSurface, MGLSwapBuffers, + }; + + return mgl_onscreen_thunks; +} + +// Use this type for the function found by dynamically discovering it in +// a DSO linked with mojo_system. For example: +// MojoSetMGLOnscreenThunksFn mojo_set_gles2_thunks_fn = +// reinterpret_cast<MojoSetMGLOnscreenThunksFn>( +// app_library.GetFunctionPointer("MojoSetMGLOnscreenThunks")); +// The expected size of |mgl_thunks| is returned. +// The contents of |mgl_thunks| are copied. +typedef size_t (*MojoSetMGLOnscreenThunksFn)( + const struct MGLOnscreenThunks* mgl_thunks); + +#endif // MOJO_PUBLIC_PLATFORM_NATIVE_MGL_ONSCREEN_THUNKS_H_ diff --git a/mojo/public/platform/native/mgl_thunks.c b/mojo/public/platform/native/mgl_thunks.c new file mode 100644 index 0000000..2e8e9d8 --- /dev/null +++ b/mojo/public/platform/native/mgl_thunks.c @@ -0,0 +1,45 @@ +// Copyright 2015 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. + +#include "mojo/public/platform/native/mgl_thunks.h" + +#include <assert.h> + +#include "mojo/public/platform/native/thunk_export.h" + +static struct MGLThunks g_thunks = {0}; + +MGLContext MGLCreateContext(MGLOpenGLAPIVersion version, + MojoHandle command_buffer_handle, + MGLContext share_group, + MGLContextLostCallback lost_callback, + void* lost_callback_closure, + const struct MojoAsyncWaiter* async_waiter) { + assert(g_thunks.MGLCreateContext); + return g_thunks.MGLCreateContext( + version, command_buffer_handle, share_group, lost_callback, + lost_callback_closure, async_waiter); +} + +void MGLDestroyContext(MGLContext context) { + assert(g_thunks.MGLDestroyContext); + g_thunks.MGLDestroyContext(context); +} + +void MGLMakeCurrent(MGLContext context) { + assert(g_thunks.MGLMakeCurrent); + g_thunks.MGLMakeCurrent(context); +} + +MGLContext MGLGetCurrentContext(void) { + assert(g_thunks.MGLGetCurrentContext); + return g_thunks.MGLGetCurrentContext(); +} + +THUNK_EXPORT size_t MojoSetMGLThunks( + const struct MGLThunks* mgl_thunks) { + if (mgl_thunks->size >= sizeof(g_thunks)) + g_thunks = *mgl_thunks; + return sizeof(g_thunks); +} diff --git a/mojo/public/platform/native/mgl_thunks.h b/mojo/public/platform/native/mgl_thunks.h new file mode 100644 index 0000000..8a56474 --- /dev/null +++ b/mojo/public/platform/native/mgl_thunks.h @@ -0,0 +1,56 @@ +// Copyright 2015 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_PLATFORM_NATIVE_MGL_THUNKS_H_ +#define MOJO_PUBLIC_PLATFORM_NATIVE_MGL_THUNKS_H_ + +#include <stddef.h> + +#include "mojo/public/c/gpu/MGL/mgl.h" + +// Structure used to bind the interface which manipulates MGL contexts to a +// DSO to theose of the embedder. +// +// This is the ABI between the embedder and the DSO. It can only have new +// functions added to the end. No other changes are supported. +#pragma pack(push, 8) +struct MGLThunks { + size_t size; // Should be set to sizeof(MGLThunks). + + MGLContext (*MGLCreateContext)(MGLOpenGLAPIVersion version, + MojoHandle command_buffer_handle, + MGLContext share_group, + MGLContextLostCallback lost_callback, + void* lost_callback_closure, + const struct MojoAsyncWaiter* async_waiter); + void (*MGLDestroyContext)(MGLContext context); + void (*MGLMakeCurrent)(MGLContext context); + MGLContext (*MGLGetCurrentContext)(void); +}; +#pragma pack(pop) + +// Intended to be called from the embedder. Returns an object initialized to +// contain pointers to each of the embedder's MGLThunks functions. +inline struct MGLThunks MojoMakeMGLThunks() { + struct MGLThunks mgl_thunks = { + sizeof(struct MGLThunks), + MGLCreateContext, + MGLDestroyContext, + MGLMakeCurrent, + MGLGetCurrentContext, + }; + + return mgl_thunks; +} + +// Use this type for the function found by dynamically discovering it in +// a DSO linked with mojo_system. For example: +// MojoSetMGLThunksFn mojo_set_gles2_thunks_fn = +// reinterpret_cast<MojoSetMGLThunksFn>( +// app_library.GetFunctionPointer("MojoSetMGLThunks")); +// The expected size of |mgl_thunks| is returned. +// The contents of |mgl_thunks| are copied. +typedef size_t (*MojoSetMGLThunksFn)(const struct MGLThunks* mgl_thunks); + +#endif // MOJO_PUBLIC_PLATFORM_NATIVE_MGL_THUNKS_H_ diff --git a/mojo/public/platform/native/system_thunks.cc b/mojo/public/platform/native/system_thunks.cc new file mode 100644 index 0000000..ed3227f --- /dev/null +++ b/mojo/public/platform/native/system_thunks.cc @@ -0,0 +1,168 @@ +// 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. + +#include "mojo/public/platform/native/system_thunks.h" + +#include <assert.h> + +#include "mojo/public/platform/native/thunk_export.h" + +extern "C" { + +static MojoSystemThunks g_thunks = {0}; + +MojoTimeTicks MojoGetTimeTicksNow() { + assert(g_thunks.GetTimeTicksNow); + return g_thunks.GetTimeTicksNow(); +} + +MojoResult MojoClose(MojoHandle handle) { + assert(g_thunks.Close); + return g_thunks.Close(handle); +} + +MojoResult MojoWait(MojoHandle handle, + MojoHandleSignals signals, + MojoDeadline deadline, + struct MojoHandleSignalsState* signals_state) { + assert(g_thunks.Wait); + return g_thunks.Wait(handle, signals, deadline, signals_state); +} + +MojoResult MojoWaitMany(const MojoHandle* handles, + const MojoHandleSignals* signals, + uint32_t num_handles, + MojoDeadline deadline, + uint32_t* result_index, + struct MojoHandleSignalsState* signals_states) { + assert(g_thunks.WaitMany); + return g_thunks.WaitMany(handles, signals, num_handles, deadline, + result_index, signals_states); +} + +MojoResult MojoCreateMessagePipe(const MojoCreateMessagePipeOptions* options, + MojoHandle* message_pipe_handle0, + MojoHandle* message_pipe_handle1) { + assert(g_thunks.CreateMessagePipe); + return g_thunks.CreateMessagePipe(options, message_pipe_handle0, + message_pipe_handle1); +} + +MojoResult MojoWriteMessage(MojoHandle message_pipe_handle, + const void* bytes, + uint32_t num_bytes, + const MojoHandle* handles, + uint32_t num_handles, + MojoWriteMessageFlags flags) { + assert(g_thunks.WriteMessage); + return g_thunks.WriteMessage(message_pipe_handle, bytes, num_bytes, handles, + num_handles, flags); +} + +MojoResult MojoReadMessage(MojoHandle message_pipe_handle, + void* bytes, + uint32_t* num_bytes, + MojoHandle* handles, + uint32_t* num_handles, + MojoReadMessageFlags flags) { + assert(g_thunks.ReadMessage); + return g_thunks.ReadMessage(message_pipe_handle, bytes, num_bytes, handles, + num_handles, flags); +} + +MojoResult MojoCreateDataPipe(const MojoCreateDataPipeOptions* options, + MojoHandle* data_pipe_producer_handle, + MojoHandle* data_pipe_consumer_handle) { + assert(g_thunks.CreateDataPipe); + return g_thunks.CreateDataPipe(options, data_pipe_producer_handle, + data_pipe_consumer_handle); +} + +MojoResult MojoWriteData(MojoHandle data_pipe_producer_handle, + const void* elements, + uint32_t* num_elements, + MojoWriteDataFlags flags) { + assert(g_thunks.WriteData); + return g_thunks.WriteData(data_pipe_producer_handle, elements, num_elements, + flags); +} + +MojoResult MojoBeginWriteData(MojoHandle data_pipe_producer_handle, + void** buffer, + uint32_t* buffer_num_elements, + MojoWriteDataFlags flags) { + assert(g_thunks.BeginWriteData); + return g_thunks.BeginWriteData(data_pipe_producer_handle, buffer, + buffer_num_elements, flags); +} + +MojoResult MojoEndWriteData(MojoHandle data_pipe_producer_handle, + uint32_t num_elements_written) { + assert(g_thunks.EndWriteData); + return g_thunks.EndWriteData(data_pipe_producer_handle, num_elements_written); +} + +MojoResult MojoReadData(MojoHandle data_pipe_consumer_handle, + void* elements, + uint32_t* num_elements, + MojoReadDataFlags flags) { + assert(g_thunks.ReadData); + return g_thunks.ReadData(data_pipe_consumer_handle, elements, num_elements, + flags); +} + +MojoResult MojoBeginReadData(MojoHandle data_pipe_consumer_handle, + const void** buffer, + uint32_t* buffer_num_elements, + MojoReadDataFlags flags) { + assert(g_thunks.BeginReadData); + return g_thunks.BeginReadData(data_pipe_consumer_handle, buffer, + buffer_num_elements, flags); +} + +MojoResult MojoEndReadData(MojoHandle data_pipe_consumer_handle, + uint32_t num_elements_read) { + assert(g_thunks.EndReadData); + return g_thunks.EndReadData(data_pipe_consumer_handle, num_elements_read); +} + +MojoResult MojoCreateSharedBuffer( + const struct MojoCreateSharedBufferOptions* options, + uint64_t num_bytes, + MojoHandle* shared_buffer_handle) { + assert(g_thunks.CreateSharedBuffer); + return g_thunks.CreateSharedBuffer(options, num_bytes, shared_buffer_handle); +} + +MojoResult MojoDuplicateBufferHandle( + MojoHandle buffer_handle, + const struct MojoDuplicateBufferHandleOptions* options, + MojoHandle* new_buffer_handle) { + assert(g_thunks.DuplicateBufferHandle); + return g_thunks.DuplicateBufferHandle(buffer_handle, options, + new_buffer_handle); +} + +MojoResult MojoMapBuffer(MojoHandle buffer_handle, + uint64_t offset, + uint64_t num_bytes, + void** buffer, + MojoMapBufferFlags flags) { + assert(g_thunks.MapBuffer); + return g_thunks.MapBuffer(buffer_handle, offset, num_bytes, buffer, flags); +} + +MojoResult MojoUnmapBuffer(void* buffer) { + assert(g_thunks.UnmapBuffer); + return g_thunks.UnmapBuffer(buffer); +} + +extern "C" THUNK_EXPORT size_t MojoSetSystemThunks( + const MojoSystemThunks* system_thunks) { + if (system_thunks->size >= sizeof(g_thunks)) + g_thunks = *system_thunks; + return sizeof(g_thunks); +} + +} // extern "C" diff --git a/mojo/public/platform/native/system_thunks.h b/mojo/public/platform/native/system_thunks.h new file mode 100644 index 0000000..bb6ca96 --- /dev/null +++ b/mojo/public/platform/native/system_thunks.h @@ -0,0 +1,146 @@ +// 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. + +// Note: This header should be compilable as C. + +#ifndef MOJO_PUBLIC_PLATFORM_NATIVE_SYSTEM_THUNKS_H_ +#define MOJO_PUBLIC_PLATFORM_NATIVE_SYSTEM_THUNKS_H_ + +#include <stddef.h> + +#include "mojo/public/c/system/core.h" + +// The embedder needs to bind the basic Mojo Core functions of a DSO to those of +// the embedder when loading a DSO that is dependent on mojo_system. +// The typical usage would look like: +// base::ScopedNativeLibrary app_library( +// base::LoadNativeLibrary(app_path_, &error)); +// typedef MojoResult (*MojoSetSystemThunksFn)(MojoSystemThunks*); +// MojoSetSystemThunksFn mojo_set_system_thunks_fn = +// reinterpret_cast<MojoSetSystemThunksFn>(app_library.GetFunctionPointer( +// "MojoSetSystemThunks")); +// MojoSystemThunks system_thunks = MojoMakeSystemThunks(); +// size_t expected_size = mojo_set_system_thunks_fn(&system_thunks); +// if (expected_size > sizeof(MojoSystemThunks)) { +// LOG(ERROR) +// << "Invalid DSO. Expected MojoSystemThunks size: " +// << expected_size; +// break; +// } + +// Structure used to bind the basic Mojo Core functions of a DSO to those of +// the embedder. +// This is the ABI between the embedder and the DSO. It can only have new +// functions added to the end. No other changes are supported. +#pragma pack(push, 8) +struct MojoSystemThunks { + size_t size; // Should be set to sizeof(MojoSystemThunks). + MojoTimeTicks (*GetTimeTicksNow)(); + MojoResult (*Close)(MojoHandle handle); + MojoResult (*Wait)(MojoHandle handle, + MojoHandleSignals signals, + MojoDeadline deadline, + struct MojoHandleSignalsState* signals_state); + MojoResult (*WaitMany)(const MojoHandle* handles, + const MojoHandleSignals* signals, + uint32_t num_handles, + MojoDeadline deadline, + uint32_t* result_index, + struct MojoHandleSignalsState* signals_states); + MojoResult (*CreateMessagePipe)( + const struct MojoCreateMessagePipeOptions* options, + MojoHandle* message_pipe_handle0, + MojoHandle* message_pipe_handle1); + MojoResult (*WriteMessage)(MojoHandle message_pipe_handle, + const void* bytes, + uint32_t num_bytes, + const MojoHandle* handles, + uint32_t num_handles, + MojoWriteMessageFlags flags); + MojoResult (*ReadMessage)(MojoHandle message_pipe_handle, + void* bytes, + uint32_t* num_bytes, + MojoHandle* handles, + uint32_t* num_handles, + MojoReadMessageFlags flags); + MojoResult (*CreateDataPipe)(const struct MojoCreateDataPipeOptions* options, + MojoHandle* data_pipe_producer_handle, + MojoHandle* data_pipe_consumer_handle); + MojoResult (*WriteData)(MojoHandle data_pipe_producer_handle, + const void* elements, + uint32_t* num_elements, + MojoWriteDataFlags flags); + MojoResult (*BeginWriteData)(MojoHandle data_pipe_producer_handle, + void** buffer, + uint32_t* buffer_num_elements, + MojoWriteDataFlags flags); + MojoResult (*EndWriteData)(MojoHandle data_pipe_producer_handle, + uint32_t num_elements_written); + MojoResult (*ReadData)(MojoHandle data_pipe_consumer_handle, + void* elements, + uint32_t* num_elements, + MojoReadDataFlags flags); + MojoResult (*BeginReadData)(MojoHandle data_pipe_consumer_handle, + const void** buffer, + uint32_t* buffer_num_elements, + MojoReadDataFlags flags); + MojoResult (*EndReadData)(MojoHandle data_pipe_consumer_handle, + uint32_t num_elements_read); + MojoResult (*CreateSharedBuffer)( + const struct MojoCreateSharedBufferOptions* options, + uint64_t num_bytes, + MojoHandle* shared_buffer_handle); + MojoResult (*DuplicateBufferHandle)( + MojoHandle buffer_handle, + const struct MojoDuplicateBufferHandleOptions* options, + MojoHandle* new_buffer_handle); + MojoResult (*MapBuffer)(MojoHandle buffer_handle, + uint64_t offset, + uint64_t num_bytes, + void** buffer, + MojoMapBufferFlags flags); + MojoResult (*UnmapBuffer)(void* buffer); +}; +#pragma pack(pop) + + +#ifdef __cplusplus +// Intended to be called from the embedder. Returns a |MojoCore| initialized +// to contain pointers to each of the embedder's MojoCore functions. +inline MojoSystemThunks MojoMakeSystemThunks() { + MojoSystemThunks system_thunks = {sizeof(MojoSystemThunks), + MojoGetTimeTicksNow, + MojoClose, + MojoWait, + MojoWaitMany, + MojoCreateMessagePipe, + MojoWriteMessage, + MojoReadMessage, + MojoCreateDataPipe, + MojoWriteData, + MojoBeginWriteData, + MojoEndWriteData, + MojoReadData, + MojoBeginReadData, + MojoEndReadData, + MojoCreateSharedBuffer, + MojoDuplicateBufferHandle, + MojoMapBuffer, + MojoUnmapBuffer}; + return system_thunks; +} +#endif + + +// Use this type for the function found by dynamically discovering it in +// a DSO linked with mojo_system. For example: +// MojoSetSystemThunksFn mojo_set_system_thunks_fn = +// reinterpret_cast<MojoSetSystemThunksFn>(app_library.GetFunctionPointer( +// "MojoSetSystemThunks")); +// The expected size of |system_thunks} is returned. +// The contents of |system_thunks| are copied. +typedef size_t (*MojoSetSystemThunksFn)( + const struct MojoSystemThunks* system_thunks); + +#endif // MOJO_PUBLIC_PLATFORM_NATIVE_SYSTEM_THUNKS_H_ diff --git a/mojo/public/platform/native/thunk_export.h b/mojo/public/platform/native/thunk_export.h new file mode 100644 index 0000000..4ec7c85 --- /dev/null +++ b/mojo/public/platform/native/thunk_export.h @@ -0,0 +1,18 @@ +// 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. + +#ifndef MOJO_PUBLIC_PLATFORM_NATIVE_THUNK_EXPORT_H_ +#define MOJO_PUBLIC_PLATFORM_NATIVE_THUNK_EXPORT_H_ + +// Call this function by looking inside the resulting shared object and +// grabbing the symbol manually. +// +// Always export this api. +#if defined(WIN32) +#define THUNK_EXPORT __declspec(dllexport) +#else +#define THUNK_EXPORT __attribute__((visibility("default"))) +#endif + +#endif // MOJO_PUBLIC_PLATFORM_NATIVE_THUNK_EXPORT_H_ diff --git a/mojo/public/tests/test_support_private.cc b/mojo/public/tests/test_support_private.cc new file mode 100644 index 0000000..0081984 --- /dev/null +++ b/mojo/public/tests/test_support_private.cc @@ -0,0 +1,76 @@ +// 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. + +#include "mojo/public/tests/test_support_private.h" + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> + +static mojo::test::TestSupport* g_test_support = NULL; + +extern "C" { + +void MojoTestSupportLogPerfResult(const char* test_name, + const char* sub_test_name, + double value, + const char* units) { + if (g_test_support) { + g_test_support->LogPerfResult(test_name, sub_test_name, value, units); + } else { + if (sub_test_name) { + printf("[no test runner]\t%s/%s\t%g\t%s\n", test_name, sub_test_name, + value, units); + } else { + printf("[no test runner]\t%s\t%g\t%s\n", test_name, value, units); + } + } +} + +FILE* MojoTestSupportOpenSourceRootRelativeFile(const char* relative_path) { + if (g_test_support) + return g_test_support->OpenSourceRootRelativeFile(relative_path); + printf("[no test runner]\n"); + return NULL; +} + +char** MojoTestSupportEnumerateSourceRootRelativeDirectory( + const char* relative_path) { + if (g_test_support) + return g_test_support->EnumerateSourceRootRelativeDirectory(relative_path); + + printf("[no test runner]\n"); + + // Return empty list: + char** rv = static_cast<char**>(calloc(1, sizeof(char*))); + rv[0] = NULL; + return rv; +} + +} // extern "C" + +namespace mojo { +namespace test { + +TestSupport::~TestSupport() { +} + +// static +void TestSupport::Init(TestSupport* test_support) { + assert(!g_test_support); + g_test_support = test_support; +} + +// static +TestSupport* TestSupport::Get() { + return g_test_support; +} + +// static +void TestSupport::Reset() { + g_test_support = NULL; +} + +} // namespace test +} // namespace mojo diff --git a/mojo/public/tests/test_support_private.h b/mojo/public/tests/test_support_private.h new file mode 100644 index 0000000..d72e158 --- /dev/null +++ b/mojo/public/tests/test_support_private.h @@ -0,0 +1,37 @@ +// 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. + +#ifndef MOJO_PUBLIC_TESTS_TEST_SUPPORT_PRIVATE_H_ +#define MOJO_PUBLIC_TESTS_TEST_SUPPORT_PRIVATE_H_ + +#include <stdio.h> + +#include "mojo/public/c/test_support/test_support.h" + +namespace mojo { +namespace test { + +// Implementors of the test support APIs can use this interface to install their +// implementation into the mojo_test_support dynamic library. +class MOJO_TEST_SUPPORT_EXPORT TestSupport { + public: + virtual ~TestSupport(); + + static void Init(TestSupport* test_support); + static TestSupport* Get(); + static void Reset(); + + virtual void LogPerfResult(const char* test_name, + const char* sub_test_name, + double value, + const char* units) = 0; + virtual FILE* OpenSourceRootRelativeFile(const char* relative_path) = 0; + virtual char** EnumerateSourceRootRelativeDirectory( + const char* relative_path) = 0; +}; + +} // namespace test +} // namespace mojo + +#endif // MOJO_PUBLIC_TESTS_TEST_SUPPORT_PRIVATE_H_ diff --git a/mojo/public/third_party/README.txt b/mojo/public/third_party/README.txt new file mode 100644 index 0000000..87d5cbb --- /dev/null +++ b/mojo/public/third_party/README.txt @@ -0,0 +1,2 @@ +This directory contains bundled third-party dependencies of the Mojo public +SDK. diff --git a/mojo/public/third_party/jinja2/AUTHORS b/mojo/public/third_party/jinja2/AUTHORS new file mode 100644 index 0000000..943f625 --- /dev/null +++ b/mojo/public/third_party/jinja2/AUTHORS @@ -0,0 +1,33 @@ +Jinja is written and maintained by the Jinja Team and various +contributors: + +Lead Developer: + +- Armin Ronacher <armin.ronacher@active-4.com> + +Developers: + +- Christoph Hack +- Georg Brandl + +Contributors: + +- Bryan McLemore +- Mickaël Guérin <kael@crocobox.org> +- Cameron Knight +- Lawrence Journal-World. +- David Cramer + +Patches and suggestions: + +- Ronny Pfannschmidt +- Axel Böhm +- Alexey Melchakov +- Bryan McLemore +- Clovis Fabricio (nosklo) +- Cameron Knight +- Peter van Dijk (Habbie) +- Stefan Ebner +- Rene Leonhardt +- Thomas Waldmann +- Cory Benfield (Lukasa) diff --git a/mojo/public/third_party/jinja2/Jinja2-2.7.1.tar.gz.md5 b/mojo/public/third_party/jinja2/Jinja2-2.7.1.tar.gz.md5 new file mode 100644 index 0000000..5c9e7579 --- /dev/null +++ b/mojo/public/third_party/jinja2/Jinja2-2.7.1.tar.gz.md5 @@ -0,0 +1 @@ +282aed153e69f970d6e76f78ed9d027a Jinja2-2.7.1.tar.gz diff --git a/mojo/public/third_party/jinja2/Jinja2-2.7.1.tar.gz.sha512 b/mojo/public/third_party/jinja2/Jinja2-2.7.1.tar.gz.sha512 new file mode 100644 index 0000000..44b486d --- /dev/null +++ b/mojo/public/third_party/jinja2/Jinja2-2.7.1.tar.gz.sha512 @@ -0,0 +1 @@ +c5d4262f6dfec77c74496f0b3afd88a37fc0573133810cfdc29fadbd9d02bb7af10b2a3ddf3075f8b682629cd41a949dcbccb293b84b0aeff9090b0aa9669e02 Jinja2-2.7.1.tar.gz diff --git a/mojo/public/third_party/jinja2/LICENSE b/mojo/public/third_party/jinja2/LICENSE new file mode 100644 index 0000000..31bf900 --- /dev/null +++ b/mojo/public/third_party/jinja2/LICENSE @@ -0,0 +1,31 @@ +Copyright (c) 2009 by the Jinja Team, see AUTHORS for more details. + +Some rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * The names of the contributors may not be used to endorse or + promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/mojo/public/third_party/jinja2/OWNERS b/mojo/public/third_party/jinja2/OWNERS new file mode 100644 index 0000000..8edbdf8 --- /dev/null +++ b/mojo/public/third_party/jinja2/OWNERS @@ -0,0 +1,3 @@ +timloh@chromium.org +haraken@chromium.org +nbarth@chromium.org diff --git a/mojo/public/third_party/jinja2/README.chromium b/mojo/public/third_party/jinja2/README.chromium new file mode 100644 index 0000000..9ab2426 --- /dev/null +++ b/mojo/public/third_party/jinja2/README.chromium @@ -0,0 +1,25 @@ +Name: Jinja2 Python Template Engine +Short Name: jinja2 +URL: http://jinja.pocoo.org/ +Version: 2.7.1 +License: BSD 3-clause License +License File: NOT_SHIPPED +Security Critical: no + +Description: +Template engine for code generation in Blink. + +Source: https://pypi.python.org/packages/source/J/Jinja2/Jinja2-2.7.1.tar.gz +MD5: 282aed153e69f970d6e76f78ed9d027a +SHA-1: a9b24d887f2be772921b3ee30a0b9d435cffadda + +Local Modifications: +This only includes the jinja2 directory from the tarball and the LICENSE and +AUTHORS files. Unit tests (testsuite directory) have been removed. +Additional chromium-specific files are: +* README.chromium (this file) +* OWNERS +* install script (get_jinja2.sh) +* files of hashes (MD5 is also posted on website, SHA-512 computed locally). +Script checks hash then unpacks archive and installs desired files. +Retrieve or update by executing jinja2/get_jinja2.sh from third_party. diff --git a/mojo/public/third_party/jinja2/__init__.py b/mojo/public/third_party/jinja2/__init__.py new file mode 100644 index 0000000..6fa11c3 --- /dev/null +++ b/mojo/public/third_party/jinja2/__init__.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +""" + jinja2 + ~~~~~~ + + Jinja2 is a template engine written in pure Python. It provides a + Django inspired non-XML syntax but supports inline expressions and + an optional sandboxed environment. + + Nutshell + -------- + + Here a small example of a Jinja2 template:: + + {% extends 'base.html' %} + {% block title %}Memberlist{% endblock %} + {% block content %} + <ul> + {% for user in users %} + <li><a href="{{ user.url }}">{{ user.username }}</a></li> + {% endfor %} + </ul> + {% endblock %} + + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +__docformat__ = 'restructuredtext en' +__version__ = '2.7.1' + +# high level interface +from jinja2.environment import Environment, Template + +# loaders +from jinja2.loaders import BaseLoader, FileSystemLoader, PackageLoader, \ + DictLoader, FunctionLoader, PrefixLoader, ChoiceLoader, \ + ModuleLoader + +# bytecode caches +from jinja2.bccache import BytecodeCache, FileSystemBytecodeCache, \ + MemcachedBytecodeCache + +# undefined types +from jinja2.runtime import Undefined, DebugUndefined, StrictUndefined + +# exceptions +from jinja2.exceptions import TemplateError, UndefinedError, \ + TemplateNotFound, TemplatesNotFound, TemplateSyntaxError, \ + TemplateAssertionError + +# decorators and public utilities +from jinja2.filters import environmentfilter, contextfilter, \ + evalcontextfilter +from jinja2.utils import Markup, escape, clear_caches, \ + environmentfunction, evalcontextfunction, contextfunction, \ + is_undefined + +__all__ = [ + 'Environment', 'Template', 'BaseLoader', 'FileSystemLoader', + 'PackageLoader', 'DictLoader', 'FunctionLoader', 'PrefixLoader', + 'ChoiceLoader', 'BytecodeCache', 'FileSystemBytecodeCache', + 'MemcachedBytecodeCache', 'Undefined', 'DebugUndefined', + 'StrictUndefined', 'TemplateError', 'UndefinedError', 'TemplateNotFound', + 'TemplatesNotFound', 'TemplateSyntaxError', 'TemplateAssertionError', + 'ModuleLoader', 'environmentfilter', 'contextfilter', 'Markup', 'escape', + 'environmentfunction', 'contextfunction', 'clear_caches', 'is_undefined', + 'evalcontextfilter', 'evalcontextfunction' +] diff --git a/mojo/public/third_party/jinja2/_compat.py b/mojo/public/third_party/jinja2/_compat.py new file mode 100644 index 0000000..8fa8a49 --- /dev/null +++ b/mojo/public/third_party/jinja2/_compat.py @@ -0,0 +1,150 @@ +# -*- coding: utf-8 -*- +""" + jinja2._compat + ~~~~~~~~~~~~~~ + + Some py2/py3 compatibility support based on a stripped down + version of six so we don't have to depend on a specific version + of it. + + :copyright: Copyright 2013 by the Jinja team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" +import sys + +PY2 = sys.version_info[0] == 2 +PYPY = hasattr(sys, 'pypy_translation_info') +_identity = lambda x: x + + +if not PY2: + unichr = chr + range_type = range + text_type = str + string_types = (str,) + + iterkeys = lambda d: iter(d.keys()) + itervalues = lambda d: iter(d.values()) + iteritems = lambda d: iter(d.items()) + + import pickle + from io import BytesIO, StringIO + NativeStringIO = StringIO + + def reraise(tp, value, tb=None): + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + + ifilter = filter + imap = map + izip = zip + intern = sys.intern + + implements_iterator = _identity + implements_to_string = _identity + encode_filename = _identity + get_next = lambda x: x.__next__ + +else: + unichr = unichr + text_type = unicode + range_type = xrange + string_types = (str, unicode) + + iterkeys = lambda d: d.iterkeys() + itervalues = lambda d: d.itervalues() + iteritems = lambda d: d.iteritems() + + import cPickle as pickle + from cStringIO import StringIO as BytesIO, StringIO + NativeStringIO = BytesIO + + exec('def reraise(tp, value, tb=None):\n raise tp, value, tb') + + from itertools import imap, izip, ifilter + intern = intern + + def implements_iterator(cls): + cls.next = cls.__next__ + del cls.__next__ + return cls + + def implements_to_string(cls): + cls.__unicode__ = cls.__str__ + cls.__str__ = lambda x: x.__unicode__().encode('utf-8') + return cls + + get_next = lambda x: x.next + + def encode_filename(filename): + if isinstance(filename, unicode): + return filename.encode('utf-8') + return filename + +try: + next = next +except NameError: + def next(it): + return it.next() + + +def with_metaclass(meta, *bases): + # This requires a bit of explanation: the basic idea is to make a + # dummy metaclass for one level of class instanciation that replaces + # itself with the actual metaclass. Because of internal type checks + # we also need to make sure that we downgrade the custom metaclass + # for one level to something closer to type (that's why __call__ and + # __init__ comes back from type etc.). + # + # This has the advantage over six.with_metaclass in that it does not + # introduce dummy classes into the final MRO. + class metaclass(meta): + __call__ = type.__call__ + __init__ = type.__init__ + def __new__(cls, name, this_bases, d): + if this_bases is None: + return type.__new__(cls, name, (), d) + return meta(name, bases, d) + return metaclass('temporary_class', None, {}) + + +try: + from collections import Mapping as mapping_types +except ImportError: + import UserDict + mapping_types = (UserDict.UserDict, UserDict.DictMixin, dict) + + +# common types. These do exist in the special types module too which however +# does not exist in IronPython out of the box. Also that way we don't have +# to deal with implementation specific stuff here +class _C(object): + def method(self): pass +def _func(): + yield None +function_type = type(_func) +generator_type = type(_func()) +method_type = type(_C().method) +code_type = type(_C.method.__code__) +try: + raise TypeError() +except TypeError: + _tb = sys.exc_info()[2] + traceback_type = type(_tb) + frame_type = type(_tb.tb_frame) + + +try: + from urllib.parse import quote_from_bytes as url_quote +except ImportError: + from urllib import quote as url_quote + + +try: + from thread import allocate_lock +except ImportError: + try: + from threading import Lock as allocate_lock + except ImportError: + from dummy_thread import allocate_lock diff --git a/mojo/public/third_party/jinja2/_stringdefs.py b/mojo/public/third_party/jinja2/_stringdefs.py new file mode 100644 index 0000000..da5830e --- /dev/null +++ b/mojo/public/third_party/jinja2/_stringdefs.py @@ -0,0 +1,132 @@ +# -*- coding: utf-8 -*- +""" + jinja2._stringdefs + ~~~~~~~~~~~~~~~~~~ + + Strings of all Unicode characters of a certain category. + Used for matching in Unicode-aware languages. Run to regenerate. + + Inspired by chartypes_create.py from the MoinMoin project, original + implementation from Pygments. + + :copyright: Copyright 2006-2009 by the Jinja team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from jinja2._compat import unichr + +Cc = u'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f' + +Cf = u'\xad\u0600\u0601\u0602\u0603\u06dd\u070f\u17b4\u17b5\u200b\u200c\u200d\u200e\u200f\u202a\u202b\u202c\u202d\u202e\u2060\u2061\u2062\u2063\u206a\u206b\u206c\u206d\u206e\u206f\ufeff\ufff9\ufffa\ufffb' + +Cn = u'\u0242\u0243\u0244\u0245\u0246\u0247\u0248\u0249\u024a\u024b\u024c\u024d\u024e\u024f\u0370\u0371\u0372\u0373\u0376\u0377\u0378\u0379\u037b\u037c\u037d\u037f\u0380\u0381\u0382\u0383\u038b\u038d\u03a2\u03cf\u0487\u04cf\u04fa\u04fb\u04fc\u04fd\u04fe\u04ff\u0510\u0511\u0512\u0513\u0514\u0515\u0516\u0517\u0518\u0519\u051a\u051b\u051c\u051d\u051e\u051f\u0520\u0521\u0522\u0523\u0524\u0525\u0526\u0527\u0528\u0529\u052a\u052b\u052c\u052d\u052e\u052f\u0530\u0557\u0558\u0560\u0588\u058b\u058c\u058d\u058e\u058f\u0590\u05ba\u05c8\u05c9\u05ca\u05cb\u05cc\u05cd\u05ce\u05cf\u05eb\u05ec\u05ed\u05ee\u05ef\u05f5\u05f6\u05f7\u05f8\u05f9\u05fa\u05fb\u05fc\u05fd\u05fe\u05ff\u0604\u0605\u0606\u0607\u0608\u0609\u060a\u0616\u0617\u0618\u0619\u061a\u061c\u061d\u0620\u063b\u063c\u063d\u063e\u063f\u065f\u070e\u074b\u074c\u076e\u076f\u0770\u0771\u0772\u0773\u0774\u0775\u0776\u0777\u0778\u0779\u077a\u077b\u077c\u077d\u077e\u077f\u07b2\u07b3\u07b4\u07b5\u07b6\u07b7\u07b8\u07b9\u07ba\u07bb\u07bc\u07bd\u07be\u07bf\u07c0\u07c1\u07c2\u07c3\u07c4\u07c5\u07c6\u07c7\u07c8\u07c9\u07ca\u07cb\u07cc\u07cd\u07ce\u07cf\u07d0\u07d1\u07d2\u07d3\u07d4\u07d5\u07d6\u07d7\u07d8\u07d9\u07da\u07db\u07dc\u07dd\u07de\u07df\u07e0\u07e1\u07e2\u07e3\u07e4\u07e5\u07e6\u07e7\u07e8\u07e9\u07ea\u07eb\u07ec\u07ed\u07ee\u07ef\u07f0\u07f1\u07f2\u07f3\u07f4\u07f5\u07f6\u07f7\u07f8\u07f9\u07fa\u07fb\u07fc\u07fd\u07fe\u07ff\u0800\u0801\u0802\u0803\u0804\u0805\u0806\u0807\u0808\u0809\u080a\u080b\u080c\u080d\u080e\u080f\u0810\u0811\u0812\u0813\u0814\u0815\u0816\u0817\u0818\u0819\u081a\u081b\u081c\u081d\u081e\u081f\u0820\u0821\u0822\u0823\u0824\u0825\u0826\u0827\u0828\u0829\u082a\u082b\u082c\u082d\u082e\u082f\u0830\u0831\u0832\u0833\u0834\u0835\u0836\u0837\u0838\u0839\u083a\u083b\u083c\u083d\u083e\u083f\u0840\u0841\u0842\u0843\u0844\u0845\u0846\u0847\u0848\u0849\u084a\u084b\u084c\u084d\u084e\u084f\u0850\u0851\u0852\u0853\u0854\u0855\u0856\u0857\u0858\u0859\u085a\u085b\u085c\u085d\u085e\u085f\u0860\u0861\u0862\u0863\u0864\u0865\u0866\u0867\u0868\u0869\u086a\u086b\u086c\u086d\u086e\u086f\u0870\u0871\u0872\u0873\u0874\u0875\u0876\u0877\u0878\u0879\u087a\u087b\u087c\u087d\u087e\u087f\u0880\u0881\u0882\u0883\u0884\u0885\u0886\u0887\u0888\u0889\u088a\u088b\u088c\u088d\u088e\u088f\u0890\u0891\u0892\u0893\u0894\u0895\u0896\u0897\u0898\u0899\u089a\u089b\u089c\u089d\u089e\u089f\u08a0\u08a1\u08a2\u08a3\u08a4\u08a5\u08a6\u08a7\u08a8\u08a9\u08aa\u08ab\u08ac\u08ad\u08ae\u08af\u08b0\u08b1\u08b2\u08b3\u08b4\u08b5\u08b6\u08b7\u08b8\u08b9\u08ba\u08bb\u08bc\u08bd\u08be\u08bf\u08c0\u08c1\u08c2\u08c3\u08c4\u08c5\u08c6\u08c7\u08c8\u08c9\u08ca\u08cb\u08cc\u08cd\u08ce\u08cf\u08d0\u08d1\u08d2\u08d3\u08d4\u08d5\u08d6\u08d7\u08d8\u08d9\u08da\u08db\u08dc\u08dd\u08de\u08df\u08e0\u08e1\u08e2\u08e3\u08e4\u08e5\u08e6\u08e7\u08e8\u08e9\u08ea\u08eb\u08ec\u08ed\u08ee\u08ef\u08f0\u08f1\u08f2\u08f3\u08f4\u08f5\u08f6\u08f7\u08f8\u08f9\u08fa\u08fb\u08fc\u08fd\u08fe\u08ff\u0900\u093a\u093b\u094e\u094f\u0955\u0956\u0957\u0971\u0972\u0973\u0974\u0975\u0976\u0977\u0978\u0979\u097a\u097b\u097c\u097e\u097f\u0980\u0984\u098d\u098e\u0991\u0992\u09a9\u09b1\u09b3\u09b4\u09b5\u09ba\u09bb\u09c5\u09c6\u09c9\u09ca\u09cf\u09d0\u09d1\u09d2\u09d3\u09d4\u09d5\u09d6\u09d8\u09d9\u09da\u09db\u09de\u09e4\u09e5\u09fb\u09fc\u09fd\u09fe\u09ff\u0a00\u0a04\u0a0b\u0a0c\u0a0d\u0a0e\u0a11\u0a12\u0a29\u0a31\u0a34\u0a37\u0a3a\u0a3b\u0a3d\u0a43\u0a44\u0a45\u0a46\u0a49\u0a4a\u0a4e\u0a4f\u0a50\u0a51\u0a52\u0a53\u0a54\u0a55\u0a56\u0a57\u0a58\u0a5d\u0a5f\u0a60\u0a61\u0a62\u0a63\u0a64\u0a65\u0a75\u0a76\u0a77\u0a78\u0a79\u0a7a\u0a7b\u0a7c\u0a7d\u0a7e\u0a7f\u0a80\u0a84\u0a8e\u0a92\u0aa9\u0ab1\u0ab4\u0aba\u0abb\u0ac6\u0aca\u0ace\u0acf\u0ad1\u0ad2\u0ad3\u0ad4\u0ad5\u0ad6\u0ad7\u0ad8\u0ad9\u0ada\u0adb\u0adc\u0add\u0ade\u0adf\u0ae4\u0ae5\u0af0\u0af2\u0af3\u0af4\u0af5\u0af6\u0af7\u0af8\u0af9\u0afa\u0afb\u0afc\u0afd\u0afe\u0aff\u0b00\u0b04\u0b0d\u0b0e\u0b11\u0b12\u0b29\u0b31\u0b34\u0b3a\u0b3b\u0b44\u0b45\u0b46\u0b49\u0b4a\u0b4e\u0b4f\u0b50\u0b51\u0b52\u0b53\u0b54\u0b55\u0b58\u0b59\u0b5a\u0b5b\u0b5e\u0b62\u0b63\u0b64\u0b65\u0b72\u0b73\u0b74\u0b75\u0b76\u0b77\u0b78\u0b79\u0b7a\u0b7b\u0b7c\u0b7d\u0b7e\u0b7f\u0b80\u0b81\u0b84\u0b8b\u0b8c\u0b8d\u0b91\u0b96\u0b97\u0b98\u0b9b\u0b9d\u0ba0\u0ba1\u0ba2\u0ba5\u0ba6\u0ba7\u0bab\u0bac\u0bad\u0bba\u0bbb\u0bbc\u0bbd\u0bc3\u0bc4\u0bc5\u0bc9\u0bce\u0bcf\u0bd0\u0bd1\u0bd2\u0bd3\u0bd4\u0bd5\u0bd6\u0bd8\u0bd9\u0bda\u0bdb\u0bdc\u0bdd\u0bde\u0bdf\u0be0\u0be1\u0be2\u0be3\u0be4\u0be5\u0bfb\u0bfc\u0bfd\u0bfe\u0bff\u0c00\u0c04\u0c0d\u0c11\u0c29\u0c34\u0c3a\u0c3b\u0c3c\u0c3d\u0c45\u0c49\u0c4e\u0c4f\u0c50\u0c51\u0c52\u0c53\u0c54\u0c57\u0c58\u0c59\u0c5a\u0c5b\u0c5c\u0c5d\u0c5e\u0c5f\u0c62\u0c63\u0c64\u0c65\u0c70\u0c71\u0c72\u0c73\u0c74\u0c75\u0c76\u0c77\u0c78\u0c79\u0c7a\u0c7b\u0c7c\u0c7d\u0c7e\u0c7f\u0c80\u0c81\u0c84\u0c8d\u0c91\u0ca9\u0cb4\u0cba\u0cbb\u0cc5\u0cc9\u0cce\u0ccf\u0cd0\u0cd1\u0cd2\u0cd3\u0cd4\u0cd7\u0cd8\u0cd9\u0cda\u0cdb\u0cdc\u0cdd\u0cdf\u0ce2\u0ce3\u0ce4\u0ce5\u0cf0\u0cf1\u0cf2\u0cf3\u0cf4\u0cf5\u0cf6\u0cf7\u0cf8\u0cf9\u0cfa\u0cfb\u0cfc\u0cfd\u0cfe\u0cff\u0d00\u0d01\u0d04\u0d0d\u0d11\u0d29\u0d3a\u0d3b\u0d3c\u0d3d\u0d44\u0d45\u0d49\u0d4e\u0d4f\u0d50\u0d51\u0d52\u0d53\u0d54\u0d55\u0d56\u0d58\u0d59\u0d5a\u0d5b\u0d5c\u0d5d\u0d5e\u0d5f\u0d62\u0d63\u0d64\u0d65\u0d70\u0d71\u0d72\u0d73\u0d74\u0d75\u0d76\u0d77\u0d78\u0d79\u0d7a\u0d7b\u0d7c\u0d7d\u0d7e\u0d7f\u0d80\u0d81\u0d84\u0d97\u0d98\u0d99\u0db2\u0dbc\u0dbe\u0dbf\u0dc7\u0dc8\u0dc9\u0dcb\u0dcc\u0dcd\u0dce\u0dd5\u0dd7\u0de0\u0de1\u0de2\u0de3\u0de4\u0de5\u0de6\u0de7\u0de8\u0de9\u0dea\u0deb\u0dec\u0ded\u0dee\u0def\u0df0\u0df1\u0df5\u0df6\u0df7\u0df8\u0df9\u0dfa\u0dfb\u0dfc\u0dfd\u0dfe\u0dff\u0e00\u0e3b\u0e3c\u0e3d\u0e3e\u0e5c\u0e5d\u0e5e\u0e5f\u0e60\u0e61\u0e62\u0e63\u0e64\u0e65\u0e66\u0e67\u0e68\u0e69\u0e6a\u0e6b\u0e6c\u0e6d\u0e6e\u0e6f\u0e70\u0e71\u0e72\u0e73\u0e74\u0e75\u0e76\u0e77\u0e78\u0e79\u0e7a\u0e7b\u0e7c\u0e7d\u0e7e\u0e7f\u0e80\u0e83\u0e85\u0e86\u0e89\u0e8b\u0e8c\u0e8e\u0e8f\u0e90\u0e91\u0e92\u0e93\u0e98\u0ea0\u0ea4\u0ea6\u0ea8\u0ea9\u0eac\u0eba\u0ebe\u0ebf\u0ec5\u0ec7\u0ece\u0ecf\u0eda\u0edb\u0ede\u0edf\u0ee0\u0ee1\u0ee2\u0ee3\u0ee4\u0ee5\u0ee6\u0ee7\u0ee8\u0ee9\u0eea\u0eeb\u0eec\u0eed\u0eee\u0eef\u0ef0\u0ef1\u0ef2\u0ef3\u0ef4\u0ef5\u0ef6\u0ef7\u0ef8\u0ef9\u0efa\u0efb\u0efc\u0efd\u0efe\u0eff\u0f48\u0f6b\u0f6c\u0f6d\u0f6e\u0f6f\u0f70\u0f8c\u0f8d\u0f8e\u0f8f\u0f98\u0fbd\u0fcd\u0fce\u0fd2\u0fd3\u0fd4\u0fd5\u0fd6\u0fd7\u0fd8\u0fd9\u0fda\u0fdb\u0fdc\u0fdd\u0fde\u0fdf\u0fe0\u0fe1\u0fe2\u0fe3\u0fe4\u0fe5\u0fe6\u0fe7\u0fe8\u0fe9\u0fea\u0feb\u0fec\u0fed\u0fee\u0fef\u0ff0\u0ff1\u0ff2\u0ff3\u0ff4\u0ff5\u0ff6\u0ff7\u0ff8\u0ff9\u0ffa\u0ffb\u0ffc\u0ffd\u0ffe\u0fff\u1022\u1028\u102b\u1033\u1034\u1035\u103a\u103b\u103c\u103d\u103e\u103f\u105a\u105b\u105c\u105d\u105e\u105f\u1060\u1061\u1062\u1063\u1064\u1065\u1066\u1067\u1068\u1069\u106a\u106b\u106c\u106d\u106e\u106f\u1070\u1071\u1072\u1073\u1074\u1075\u1076\u1077\u1078\u1079\u107a\u107b\u107c\u107d\u107e\u107f\u1080\u1081\u1082\u1083\u1084\u1085\u1086\u1087\u1088\u1089\u108a\u108b\u108c\u108d\u108e\u108f\u1090\u1091\u1092\u1093\u1094\u1095\u1096\u1097\u1098\u1099\u109a\u109b\u109c\u109d\u109e\u109f\u10c6\u10c7\u10c8\u10c9\u10ca\u10cb\u10cc\u10cd\u10ce\u10cf\u10fd\u10fe\u10ff\u115a\u115b\u115c\u115d\u115e\u11a3\u11a4\u11a5\u11a6\u11a7\u11fa\u11fb\u11fc\u11fd\u11fe\u11ff\u1249\u124e\u124f\u1257\u1259\u125e\u125f\u1289\u128e\u128f\u12b1\u12b6\u12b7\u12bf\u12c1\u12c6\u12c7\u12d7\u1311\u1316\u1317\u135b\u135c\u135d\u135e\u137d\u137e\u137f\u139a\u139b\u139c\u139d\u139e\u139f\u13f5\u13f6\u13f7\u13f8\u13f9\u13fa\u13fb\u13fc\u13fd\u13fe\u13ff\u1400\u1677\u1678\u1679\u167a\u167b\u167c\u167d\u167e\u167f\u169d\u169e\u169f\u16f1\u16f2\u16f3\u16f4\u16f5\u16f6\u16f7\u16f8\u16f9\u16fa\u16fb\u16fc\u16fd\u16fe\u16ff\u170d\u1715\u1716\u1717\u1718\u1719\u171a\u171b\u171c\u171d\u171e\u171f\u1737\u1738\u1739\u173a\u173b\u173c\u173d\u173e\u173f\u1754\u1755\u1756\u1757\u1758\u1759\u175a\u175b\u175c\u175d\u175e\u175f\u176d\u1771\u1774\u1775\u1776\u1777\u1778\u1779\u177a\u177b\u177c\u177d\u177e\u177f\u17de\u17df\u17ea\u17eb\u17ec\u17ed\u17ee\u17ef\u17fa\u17fb\u17fc\u17fd\u17fe\u17ff\u180f\u181a\u181b\u181c\u181d\u181e\u181f\u1878\u1879\u187a\u187b\u187c\u187d\u187e\u187f\u18aa\u18ab\u18ac\u18ad\u18ae\u18af\u18b0\u18b1\u18b2\u18b3\u18b4\u18b5\u18b6\u18b7\u18b8\u18b9\u18ba\u18bb\u18bc\u18bd\u18be\u18bf\u18c0\u18c1\u18c2\u18c3\u18c4\u18c5\u18c6\u18c7\u18c8\u18c9\u18ca\u18cb\u18cc\u18cd\u18ce\u18cf\u18d0\u18d1\u18d2\u18d3\u18d4\u18d5\u18d6\u18d7\u18d8\u18d9\u18da\u18db\u18dc\u18dd\u18de\u18df\u18e0\u18e1\u18e2\u18e3\u18e4\u18e5\u18e6\u18e7\u18e8\u18e9\u18ea\u18eb\u18ec\u18ed\u18ee\u18ef\u18f0\u18f1\u18f2\u18f3\u18f4\u18f5\u18f6\u18f7\u18f8\u18f9\u18fa\u18fb\u18fc\u18fd\u18fe\u18ff\u191d\u191e\u191f\u192c\u192d\u192e\u192f\u193c\u193d\u193e\u193f\u1941\u1942\u1943\u196e\u196f\u1975\u1976\u1977\u1978\u1979\u197a\u197b\u197c\u197d\u197e\u197f\u19aa\u19ab\u19ac\u19ad\u19ae\u19af\u19ca\u19cb\u19cc\u19cd\u19ce\u19cf\u19da\u19db\u19dc\u19dd\u1a1c\u1a1d\u1a20\u1a21\u1a22\u1a23\u1a24\u1a25\u1a26\u1a27\u1a28\u1a29\u1a2a\u1a2b\u1a2c\u1a2d\u1a2e\u1a2f\u1a30\u1a31\u1a32\u1a33\u1a34\u1a35\u1a36\u1a37\u1a38\u1a39\u1a3a\u1a3b\u1a3c\u1a3d\u1a3e\u1a3f\u1a40\u1a41\u1a42\u1a43\u1a44\u1a45\u1a46\u1a47\u1a48\u1a49\u1a4a\u1a4b\u1a4c\u1a4d\u1a4e\u1a4f\u1a50\u1a51\u1a52\u1a53\u1a54\u1a55\u1a56\u1a57\u1a58\u1a59\u1a5a\u1a5b\u1a5c\u1a5d\u1a5e\u1a5f\u1a60\u1a61\u1a62\u1a63\u1a64\u1a65\u1a66\u1a67\u1a68\u1a69\u1a6a\u1a6b\u1a6c\u1a6d\u1a6e\u1a6f\u1a70\u1a71\u1a72\u1a73\u1a74\u1a75\u1a76\u1a77\u1a78\u1a79\u1a7a\u1a7b\u1a7c\u1a7d\u1a7e\u1a7f\u1a80\u1a81\u1a82\u1a83\u1a84\u1a85\u1a86\u1a87\u1a88\u1a89\u1a8a\u1a8b\u1a8c\u1a8d\u1a8e\u1a8f\u1a90\u1a91\u1a92\u1a93\u1a94\u1a95\u1a96\u1a97\u1a98\u1a99\u1a9a\u1a9b\u1a9c\u1a9d\u1a9e\u1a9f\u1aa0\u1aa1\u1aa2\u1aa3\u1aa4\u1aa5\u1aa6\u1aa7\u1aa8\u1aa9\u1aaa\u1aab\u1aac\u1aad\u1aae\u1aaf\u1ab0\u1ab1\u1ab2\u1ab3\u1ab4\u1ab5\u1ab6\u1ab7\u1ab8\u1ab9\u1aba\u1abb\u1abc\u1abd\u1abe\u1abf\u1ac0\u1ac1\u1ac2\u1ac3\u1ac4\u1ac5\u1ac6\u1ac7\u1ac8\u1ac9\u1aca\u1acb\u1acc\u1acd\u1ace\u1acf\u1ad0\u1ad1\u1ad2\u1ad3\u1ad4\u1ad5\u1ad6\u1ad7\u1ad8\u1ad9\u1ada\u1adb\u1adc\u1add\u1ade\u1adf\u1ae0\u1ae1\u1ae2\u1ae3\u1ae4\u1ae5\u1ae6\u1ae7\u1ae8\u1ae9\u1aea\u1aeb\u1aec\u1aed\u1aee\u1aef\u1af0\u1af1\u1af2\u1af3\u1af4\u1af5\u1af6\u1af7\u1af8\u1af9\u1afa\u1afb\u1afc\u1afd\u1afe\u1aff\u1b00\u1b01\u1b02\u1b03\u1b04\u1b05\u1b06\u1b07\u1b08\u1b09\u1b0a\u1b0b\u1b0c\u1b0d\u1b0e\u1b0f\u1b10\u1b11\u1b12\u1b13\u1b14\u1b15\u1b16\u1b17\u1b18\u1b19\u1b1a\u1b1b\u1b1c\u1b1d\u1b1e\u1b1f\u1b20\u1b21\u1b22\u1b23\u1b24\u1b25\u1b26\u1b27\u1b28\u1b29\u1b2a\u1b2b\u1b2c\u1b2d\u1b2e\u1b2f\u1b30\u1b31\u1b32\u1b33\u1b34\u1b35\u1b36\u1b37\u1b38\u1b39\u1b3a\u1b3b\u1b3c\u1b3d\u1b3e\u1b3f\u1b40\u1b41\u1b42\u1b43\u1b44\u1b45\u1b46\u1b47\u1b48\u1b49\u1b4a\u1b4b\u1b4c\u1b4d\u1b4e\u1b4f\u1b50\u1b51\u1b52\u1b53\u1b54\u1b55\u1b56\u1b57\u1b58\u1b59\u1b5a\u1b5b\u1b5c\u1b5d\u1b5e\u1b5f\u1b60\u1b61\u1b62\u1b63\u1b64\u1b65\u1b66\u1b67\u1b68\u1b69\u1b6a\u1b6b\u1b6c\u1b6d\u1b6e\u1b6f\u1b70\u1b71\u1b72\u1b73\u1b74\u1b75\u1b76\u1b77\u1b78\u1b79\u1b7a\u1b7b\u1b7c\u1b7d\u1b7e\u1b7f\u1b80\u1b81\u1b82\u1b83\u1b84\u1b85\u1b86\u1b87\u1b88\u1b89\u1b8a\u1b8b\u1b8c\u1b8d\u1b8e\u1b8f\u1b90\u1b91\u1b92\u1b93\u1b94\u1b95\u1b96\u1b97\u1b98\u1b99\u1b9a\u1b9b\u1b9c\u1b9d\u1b9e\u1b9f\u1ba0\u1ba1\u1ba2\u1ba3\u1ba4\u1ba5\u1ba6\u1ba7\u1ba8\u1ba9\u1baa\u1bab\u1bac\u1bad\u1bae\u1baf\u1bb0\u1bb1\u1bb2\u1bb3\u1bb4\u1bb5\u1bb6\u1bb7\u1bb8\u1bb9\u1bba\u1bbb\u1bbc\u1bbd\u1bbe\u1bbf\u1bc0\u1bc1\u1bc2\u1bc3\u1bc4\u1bc5\u1bc6\u1bc7\u1bc8\u1bc9\u1bca\u1bcb\u1bcc\u1bcd\u1bce\u1bcf\u1bd0\u1bd1\u1bd2\u1bd3\u1bd4\u1bd5\u1bd6\u1bd7\u1bd8\u1bd9\u1bda\u1bdb\u1bdc\u1bdd\u1bde\u1bdf\u1be0\u1be1\u1be2\u1be3\u1be4\u1be5\u1be6\u1be7\u1be8\u1be9\u1bea\u1beb\u1bec\u1bed\u1bee\u1bef\u1bf0\u1bf1\u1bf2\u1bf3\u1bf4\u1bf5\u1bf6\u1bf7\u1bf8\u1bf9\u1bfa\u1bfb\u1bfc\u1bfd\u1bfe\u1bff\u1c00\u1c01\u1c02\u1c03\u1c04\u1c05\u1c06\u1c07\u1c08\u1c09\u1c0a\u1c0b\u1c0c\u1c0d\u1c0e\u1c0f\u1c10\u1c11\u1c12\u1c13\u1c14\u1c15\u1c16\u1c17\u1c18\u1c19\u1c1a\u1c1b\u1c1c\u1c1d\u1c1e\u1c1f\u1c20\u1c21\u1c22\u1c23\u1c24\u1c25\u1c26\u1c27\u1c28\u1c29\u1c2a\u1c2b\u1c2c\u1c2d\u1c2e\u1c2f\u1c30\u1c31\u1c32\u1c33\u1c34\u1c35\u1c36\u1c37\u1c38\u1c39\u1c3a\u1c3b\u1c3c\u1c3d\u1c3e\u1c3f\u1c40\u1c41\u1c42\u1c43\u1c44\u1c45\u1c46\u1c47\u1c48\u1c49\u1c4a\u1c4b\u1c4c\u1c4d\u1c4e\u1c4f\u1c50\u1c51\u1c52\u1c53\u1c54\u1c55\u1c56\u1c57\u1c58\u1c59\u1c5a\u1c5b\u1c5c\u1c5d\u1c5e\u1c5f\u1c60\u1c61\u1c62\u1c63\u1c64\u1c65\u1c66\u1c67\u1c68\u1c69\u1c6a\u1c6b\u1c6c\u1c6d\u1c6e\u1c6f\u1c70\u1c71\u1c72\u1c73\u1c74\u1c75\u1c76\u1c77\u1c78\u1c79\u1c7a\u1c7b\u1c7c\u1c7d\u1c7e\u1c7f\u1c80\u1c81\u1c82\u1c83\u1c84\u1c85\u1c86\u1c87\u1c88\u1c89\u1c8a\u1c8b\u1c8c\u1c8d\u1c8e\u1c8f\u1c90\u1c91\u1c92\u1c93\u1c94\u1c95\u1c96\u1c97\u1c98\u1c99\u1c9a\u1c9b\u1c9c\u1c9d\u1c9e\u1c9f\u1ca0\u1ca1\u1ca2\u1ca3\u1ca4\u1ca5\u1ca6\u1ca7\u1ca8\u1ca9\u1caa\u1cab\u1cac\u1cad\u1cae\u1caf\u1cb0\u1cb1\u1cb2\u1cb3\u1cb4\u1cb5\u1cb6\u1cb7\u1cb8\u1cb9\u1cba\u1cbb\u1cbc\u1cbd\u1cbe\u1cbf\u1cc0\u1cc1\u1cc2\u1cc3\u1cc4\u1cc5\u1cc6\u1cc7\u1cc8\u1cc9\u1cca\u1ccb\u1ccc\u1ccd\u1cce\u1ccf\u1cd0\u1cd1\u1cd2\u1cd3\u1cd4\u1cd5\u1cd6\u1cd7\u1cd8\u1cd9\u1cda\u1cdb\u1cdc\u1cdd\u1cde\u1cdf\u1ce0\u1ce1\u1ce2\u1ce3\u1ce4\u1ce5\u1ce6\u1ce7\u1ce8\u1ce9\u1cea\u1ceb\u1cec\u1ced\u1cee\u1cef\u1cf0\u1cf1\u1cf2\u1cf3\u1cf4\u1cf5\u1cf6\u1cf7\u1cf8\u1cf9\u1cfa\u1cfb\u1cfc\u1cfd\u1cfe\u1cff\u1dc4\u1dc5\u1dc6\u1dc7\u1dc8\u1dc9\u1dca\u1dcb\u1dcc\u1dcd\u1dce\u1dcf\u1dd0\u1dd1\u1dd2\u1dd3\u1dd4\u1dd5\u1dd6\u1dd7\u1dd8\u1dd9\u1dda\u1ddb\u1ddc\u1ddd\u1dde\u1ddf\u1de0\u1de1\u1de2\u1de3\u1de4\u1de5\u1de6\u1de7\u1de8\u1de9\u1dea\u1deb\u1dec\u1ded\u1dee\u1def\u1df0\u1df1\u1df2\u1df3\u1df4\u1df5\u1df6\u1df7\u1df8\u1df9\u1dfa\u1dfb\u1dfc\u1dfd\u1dfe\u1dff\u1e9c\u1e9d\u1e9e\u1e9f\u1efa\u1efb\u1efc\u1efd\u1efe\u1eff\u1f16\u1f17\u1f1e\u1f1f\u1f46\u1f47\u1f4e\u1f4f\u1f58\u1f5a\u1f5c\u1f5e\u1f7e\u1f7f\u1fb5\u1fc5\u1fd4\u1fd5\u1fdc\u1ff0\u1ff1\u1ff5\u1fff\u2064\u2065\u2066\u2067\u2068\u2069\u2072\u2073\u208f\u2095\u2096\u2097\u2098\u2099\u209a\u209b\u209c\u209d\u209e\u209f\u20b6\u20b7\u20b8\u20b9\u20ba\u20bb\u20bc\u20bd\u20be\u20bf\u20c0\u20c1\u20c2\u20c3\u20c4\u20c5\u20c6\u20c7\u20c8\u20c9\u20ca\u20cb\u20cc\u20cd\u20ce\u20cf\u20ec\u20ed\u20ee\u20ef\u20f0\u20f1\u20f2\u20f3\u20f4\u20f5\u20f6\u20f7\u20f8\u20f9\u20fa\u20fb\u20fc\u20fd\u20fe\u20ff\u214d\u214e\u214f\u2150\u2151\u2152\u2184\u2185\u2186\u2187\u2188\u2189\u218a\u218b\u218c\u218d\u218e\u218f\u23dc\u23dd\u23de\u23df\u23e0\u23e1\u23e2\u23e3\u23e4\u23e5\u23e6\u23e7\u23e8\u23e9\u23ea\u23eb\u23ec\u23ed\u23ee\u23ef\u23f0\u23f1\u23f2\u23f3\u23f4\u23f5\u23f6\u23f7\u23f8\u23f9\u23fa\u23fb\u23fc\u23fd\u23fe\u23ff\u2427\u2428\u2429\u242a\u242b\u242c\u242d\u242e\u242f\u2430\u2431\u2432\u2433\u2434\u2435\u2436\u2437\u2438\u2439\u243a\u243b\u243c\u243d\u243e\u243f\u244b\u244c\u244d\u244e\u244f\u2450\u2451\u2452\u2453\u2454\u2455\u2456\u2457\u2458\u2459\u245a\u245b\u245c\u245d\u245e\u245f\u269d\u269e\u269f\u26b2\u26b3\u26b4\u26b5\u26b6\u26b7\u26b8\u26b9\u26ba\u26bb\u26bc\u26bd\u26be\u26bf\u26c0\u26c1\u26c2\u26c3\u26c4\u26c5\u26c6\u26c7\u26c8\u26c9\u26ca\u26cb\u26cc\u26cd\u26ce\u26cf\u26d0\u26d1\u26d2\u26d3\u26d4\u26d5\u26d6\u26d7\u26d8\u26d9\u26da\u26db\u26dc\u26dd\u26de\u26df\u26e0\u26e1\u26e2\u26e3\u26e4\u26e5\u26e6\u26e7\u26e8\u26e9\u26ea\u26eb\u26ec\u26ed\u26ee\u26ef\u26f0\u26f1\u26f2\u26f3\u26f4\u26f5\u26f6\u26f7\u26f8\u26f9\u26fa\u26fb\u26fc\u26fd\u26fe\u26ff\u2700\u2705\u270a\u270b\u2728\u274c\u274e\u2753\u2754\u2755\u2757\u275f\u2760\u2795\u2796\u2797\u27b0\u27bf\u27c7\u27c8\u27c9\u27ca\u27cb\u27cc\u27cd\u27ce\u27cf\u27ec\u27ed\u27ee\u27ef\u2b14\u2b15\u2b16\u2b17\u2b18\u2b19\u2b1a\u2b1b\u2b1c\u2b1d\u2b1e\u2b1f\u2b20\u2b21\u2b22\u2b23\u2b24\u2b25\u2b26\u2b27\u2b28\u2b29\u2b2a\u2b2b\u2b2c\u2b2d\u2b2e\u2b2f\u2b30\u2b31\u2b32\u2b33\u2b34\u2b35\u2b36\u2b37\u2b38\u2b39\u2b3a\u2b3b\u2b3c\u2b3d\u2b3e\u2b3f\u2b40\u2b41\u2b42\u2b43\u2b44\u2b45\u2b46\u2b47\u2b48\u2b49\u2b4a\u2b4b\u2b4c\u2b4d\u2b4e\u2b4f\u2b50\u2b51\u2b52\u2b53\u2b54\u2b55\u2b56\u2b57\u2b58\u2b59\u2b5a\u2b5b\u2b5c\u2b5d\u2b5e\u2b5f\u2b60\u2b61\u2b62\u2b63\u2b64\u2b65\u2b66\u2b67\u2b68\u2b69\u2b6a\u2b6b\u2b6c\u2b6d\u2b6e\u2b6f\u2b70\u2b71\u2b72\u2b73\u2b74\u2b75\u2b76\u2b77\u2b78\u2b79\u2b7a\u2b7b\u2b7c\u2b7d\u2b7e\u2b7f\u2b80\u2b81\u2b82\u2b83\u2b84\u2b85\u2b86\u2b87\u2b88\u2b89\u2b8a\u2b8b\u2b8c\u2b8d\u2b8e\u2b8f\u2b90\u2b91\u2b92\u2b93\u2b94\u2b95\u2b96\u2b97\u2b98\u2b99\u2b9a\u2b9b\u2b9c\u2b9d\u2b9e\u2b9f\u2ba0\u2ba1\u2ba2\u2ba3\u2ba4\u2ba5\u2ba6\u2ba7\u2ba8\u2ba9\u2baa\u2bab\u2bac\u2bad\u2bae\u2baf\u2bb0\u2bb1\u2bb2\u2bb3\u2bb4\u2bb5\u2bb6\u2bb7\u2bb8\u2bb9\u2bba\u2bbb\u2bbc\u2bbd\u2bbe\u2bbf\u2bc0\u2bc1\u2bc2\u2bc3\u2bc4\u2bc5\u2bc6\u2bc7\u2bc8\u2bc9\u2bca\u2bcb\u2bcc\u2bcd\u2bce\u2bcf\u2bd0\u2bd1\u2bd2\u2bd3\u2bd4\u2bd5\u2bd6\u2bd7\u2bd8\u2bd9\u2bda\u2bdb\u2bdc\u2bdd\u2bde\u2bdf\u2be0\u2be1\u2be2\u2be3\u2be4\u2be5\u2be6\u2be7\u2be8\u2be9\u2bea\u2beb\u2bec\u2bed\u2bee\u2bef\u2bf0\u2bf1\u2bf2\u2bf3\u2bf4\u2bf5\u2bf6\u2bf7\u2bf8\u2bf9\u2bfa\u2bfb\u2bfc\u2bfd\u2bfe\u2bff\u2c2f\u2c5f\u2c60\u2c61\u2c62\u2c63\u2c64\u2c65\u2c66\u2c67\u2c68\u2c69\u2c6a\u2c6b\u2c6c\u2c6d\u2c6e\u2c6f\u2c70\u2c71\u2c72\u2c73\u2c74\u2c75\u2c76\u2c77\u2c78\u2c79\u2c7a\u2c7b\u2c7c\u2c7d\u2c7e\u2c7f\u2ceb\u2cec\u2ced\u2cee\u2cef\u2cf0\u2cf1\u2cf2\u2cf3\u2cf4\u2cf5\u2cf6\u2cf7\u2cf8\u2d26\u2d27\u2d28\u2d29\u2d2a\u2d2b\u2d2c\u2d2d\u2d2e\u2d2f\u2d66\u2d67\u2d68\u2d69\u2d6a\u2d6b\u2d6c\u2d6d\u2d6e\u2d70\u2d71\u2d72\u2d73\u2d74\u2d75\u2d76\u2d77\u2d78\u2d79\u2d7a\u2d7b\u2d7c\u2d7d\u2d7e\u2d7f\u2d97\u2d98\u2d99\u2d9a\u2d9b\u2d9c\u2d9d\u2d9e\u2d9f\u2da7\u2daf\u2db7\u2dbf\u2dc7\u2dcf\u2dd7\u2ddf\u2de0\u2de1\u2de2\u2de3\u2de4\u2de5\u2de6\u2de7\u2de8\u2de9\u2dea\u2deb\u2dec\u2ded\u2dee\u2def\u2df0\u2df1\u2df2\u2df3\u2df4\u2df5\u2df6\u2df7\u2df8\u2df9\u2dfa\u2dfb\u2dfc\u2dfd\u2dfe\u2dff\u2e18\u2e19\u2e1a\u2e1b\u2e1e\u2e1f\u2e20\u2e21\u2e22\u2e23\u2e24\u2e25\u2e26\u2e27\u2e28\u2e29\u2e2a\u2e2b\u2e2c\u2e2d\u2e2e\u2e2f\u2e30\u2e31\u2e32\u2e33\u2e34\u2e35\u2e36\u2e37\u2e38\u2e39\u2e3a\u2e3b\u2e3c\u2e3d\u2e3e\u2e3f\u2e40\u2e41\u2e42\u2e43\u2e44\u2e45\u2e46\u2e47\u2e48\u2e49\u2e4a\u2e4b\u2e4c\u2e4d\u2e4e\u2e4f\u2e50\u2e51\u2e52\u2e53\u2e54\u2e55\u2e56\u2e57\u2e58\u2e59\u2e5a\u2e5b\u2e5c\u2e5d\u2e5e\u2e5f\u2e60\u2e61\u2e62\u2e63\u2e64\u2e65\u2e66\u2e67\u2e68\u2e69\u2e6a\u2e6b\u2e6c\u2e6d\u2e6e\u2e6f\u2e70\u2e71\u2e72\u2e73\u2e74\u2e75\u2e76\u2e77\u2e78\u2e79\u2e7a\u2e7b\u2e7c\u2e7d\u2e7e\u2e7f\u2e9a\u2ef4\u2ef5\u2ef6\u2ef7\u2ef8\u2ef9\u2efa\u2efb\u2efc\u2efd\u2efe\u2eff\u2fd6\u2fd7\u2fd8\u2fd9\u2fda\u2fdb\u2fdc\u2fdd\u2fde\u2fdf\u2fe0\u2fe1\u2fe2\u2fe3\u2fe4\u2fe5\u2fe6\u2fe7\u2fe8\u2fe9\u2fea\u2feb\u2fec\u2fed\u2fee\u2fef\u2ffc\u2ffd\u2ffe\u2fff\u3040\u3097\u3098\u3100\u3101\u3102\u3103\u3104\u312d\u312e\u312f\u3130\u318f\u31b8\u31b9\u31ba\u31bb\u31bc\u31bd\u31be\u31bf\u31d0\u31d1\u31d2\u31d3\u31d4\u31d5\u31d6\u31d7\u31d8\u31d9\u31da\u31db\u31dc\u31dd\u31de\u31df\u31e0\u31e1\u31e2\u31e3\u31e4\u31e5\u31e6\u31e7\u31e8\u31e9\u31ea\u31eb\u31ec\u31ed\u31ee\u31ef\u321f\u3244\u3245\u3246\u3247\u3248\u3249\u324a\u324b\u324c\u324d\u324e\u324f\u32ff\u4db6\u4db7\u4db8\u4db9\u4dba\u4dbb\u4dbc\u4dbd\u4dbe\u4dbf\u9fbc\u9fbd\u9fbe\u9fbf\u9fc0\u9fc1\u9fc2\u9fc3\u9fc4\u9fc5\u9fc6\u9fc7\u9fc8\u9fc9\u9fca\u9fcb\u9fcc\u9fcd\u9fce\u9fcf\u9fd0\u9fd1\u9fd2\u9fd3\u9fd4\u9fd5\u9fd6\u9fd7\u9fd8\u9fd9\u9fda\u9fdb\u9fdc\u9fdd\u9fde\u9fdf\u9fe0\u9fe1\u9fe2\u9fe3\u9fe4\u9fe5\u9fe6\u9fe7\u9fe8\u9fe9\u9fea\u9feb\u9fec\u9fed\u9fee\u9fef\u9ff0\u9ff1\u9ff2\u9ff3\u9ff4\u9ff5\u9ff6\u9ff7\u9ff8\u9ff9\u9ffa\u9ffb\u9ffc\u9ffd\u9ffe\u9fff\ua48d\ua48e\ua48f\ua4c7\ua4c8\ua4c9\ua4ca\ua4cb\ua4cc\ua4cd\ua4ce\ua4cf\ua4d0\ua4d1\ua4d2\ua4d3\ua4d4\ua4d5\ua4d6\ua4d7\ua4d8\ua4d9\ua4da\ua4db\ua4dc\ua4dd\ua4de\ua4df\ua4e0\ua4e1\ua4e2\ua4e3\ua4e4\ua4e5\ua4e6\ua4e7\ua4e8\ua4e9\ua4ea\ua4eb\ua4ec\ua4ed\ua4ee\ua4ef\ua4f0\ua4f1\ua4f2\ua4f3\ua4f4\ua4f5\ua4f6\ua4f7\ua4f8\ua4f9\ua4fa\ua4fb\ua4fc\ua4fd\ua4fe\ua4ff\ua500\ua501\ua502\ua503\ua504\ua505\ua506\ua507\ua508\ua509\ua50a\ua50b\ua50c\ua50d\ua50e\ua50f\ua510\ua511\ua512\ua513\ua514\ua515\ua516\ua517\ua518\ua519\ua51a\ua51b\ua51c\ua51d\ua51e\ua51f\ua520\ua521\ua522\ua523\ua524\ua525\ua526\ua527\ua528\ua529\ua52a\ua52b\ua52c\ua52d\ua52e\ua52f\ua530\ua531\ua532\ua533\ua534\ua535\ua536\ua537\ua538\ua539\ua53a\ua53b\ua53c\ua53d\ua53e\ua53f\ua540\ua541\ua542\ua543\ua544\ua545\ua546\ua547\ua548\ua549\ua54a\ua54b\ua54c\ua54d\ua54e\ua54f\ua550\ua551\ua552\ua553\ua554\ua555\ua556\ua557\ua558\ua559\ua55a\ua55b\ua55c\ua55d\ua55e\ua55f\ua560\ua561\ua562\ua563\ua564\ua565\ua566\ua567\ua568\ua569\ua56a\ua56b\ua56c\ua56d\ua56e\ua56f\ua570\ua571\ua572\ua573\ua574\ua575\ua576\ua577\ua578\ua579\ua57a\ua57b\ua57c\ua57d\ua57e\ua57f\ua580\ua581\ua582\ua583\ua584\ua585\ua586\ua587\ua588\ua589\ua58a\ua58b\ua58c\ua58d\ua58e\ua58f\ua590\ua591\ua592\ua593\ua594\ua595\ua596\ua597\ua598\ua599\ua59a\ua59b\ua59c\ua59d\ua59e\ua59f\ua5a0\ua5a1\ua5a2\ua5a3\ua5a4\ua5a5\ua5a6\ua5a7\ua5a8\ua5a9\ua5aa\ua5ab\ua5ac\ua5ad\ua5ae\ua5af\ua5b0\ua5b1\ua5b2\ua5b3\ua5b4\ua5b5\ua5b6\ua5b7\ua5b8\ua5b9\ua5ba\ua5bb\ua5bc\ua5bd\ua5be\ua5bf\ua5c0\ua5c1\ua5c2\ua5c3\ua5c4\ua5c5\ua5c6\ua5c7\ua5c8\ua5c9\ua5ca\ua5cb\ua5cc\ua5cd\ua5ce\ua5cf\ua5d0\ua5d1\ua5d2\ua5d3\ua5d4\ua5d5\ua5d6\ua5d7\ua5d8\ua5d9\ua5da\ua5db\ua5dc\ua5dd\ua5de\ua5df\ua5e0\ua5e1\ua5e2\ua5e3\ua5e4\ua5e5\ua5e6\ua5e7\ua5e8\ua5e9\ua5ea\ua5eb\ua5ec\ua5ed\ua5ee\ua5ef\ua5f0\ua5f1\ua5f2\ua5f3\ua5f4\ua5f5\ua5f6\ua5f7\ua5f8\ua5f9\ua5fa\ua5fb\ua5fc\ua5fd\ua5fe\ua5ff\ua600\ua601\ua602\ua603\ua604\ua605\ua606\ua607\ua608\ua609\ua60a\ua60b\ua60c\ua60d\ua60e\ua60f\ua610\ua611\ua612\ua613\ua614\ua615\ua616\ua617\ua618\ua619\ua61a\ua61b\ua61c\ua61d\ua61e\ua61f\ua620\ua621\ua622\ua623\ua624\ua625\ua626\ua627\ua628\ua629\ua62a\ua62b\ua62c\ua62d\ua62e\ua62f\ua630\ua631\ua632\ua633\ua634\ua635\ua636\ua637\ua638\ua639\ua63a\ua63b\ua63c\ua63d\ua63e\ua63f\ua640\ua641\ua642\ua643\ua644\ua645\ua646\ua647\ua648\ua649\ua64a\ua64b\ua64c\ua64d\ua64e\ua64f\ua650\ua651\ua652\ua653\ua654\ua655\ua656\ua657\ua658\ua659\ua65a\ua65b\ua65c\ua65d\ua65e\ua65f\ua660\ua661\ua662\ua663\ua664\ua665\ua666\ua667\ua668\ua669\ua66a\ua66b\ua66c\ua66d\ua66e\ua66f\ua670\ua671\ua672\ua673\ua674\ua675\ua676\ua677\ua678\ua679\ua67a\ua67b\ua67c\ua67d\ua67e\ua67f\ua680\ua681\ua682\ua683\ua684\ua685\ua686\ua687\ua688\ua689\ua68a\ua68b\ua68c\ua68d\ua68e\ua68f\ua690\ua691\ua692\ua693\ua694\ua695\ua696\ua697\ua698\ua699\ua69a\ua69b\ua69c\ua69d\ua69e\ua69f\ua6a0\ua6a1\ua6a2\ua6a3\ua6a4\ua6a5\ua6a6\ua6a7\ua6a8\ua6a9\ua6aa\ua6ab\ua6ac\ua6ad\ua6ae\ua6af\ua6b0\ua6b1\ua6b2\ua6b3\ua6b4\ua6b5\ua6b6\ua6b7\ua6b8\ua6b9\ua6ba\ua6bb\ua6bc\ua6bd\ua6be\ua6bf\ua6c0\ua6c1\ua6c2\ua6c3\ua6c4\ua6c5\ua6c6\ua6c7\ua6c8\ua6c9\ua6ca\ua6cb\ua6cc\ua6cd\ua6ce\ua6cf\ua6d0\ua6d1\ua6d2\ua6d3\ua6d4\ua6d5\ua6d6\ua6d7\ua6d8\ua6d9\ua6da\ua6db\ua6dc\ua6dd\ua6de\ua6df\ua6e0\ua6e1\ua6e2\ua6e3\ua6e4\ua6e5\ua6e6\ua6e7\ua6e8\ua6e9\ua6ea\ua6eb\ua6ec\ua6ed\ua6ee\ua6ef\ua6f0\ua6f1\ua6f2\ua6f3\ua6f4\ua6f5\ua6f6\ua6f7\ua6f8\ua6f9\ua6fa\ua6fb\ua6fc\ua6fd\ua6fe\ua6ff\ua717\ua718\ua719\ua71a\ua71b\ua71c\ua71d\ua71e\ua71f\ua720\ua721\ua722\ua723\ua724\ua725\ua726\ua727\ua728\ua729\ua72a\ua72b\ua72c\ua72d\ua72e\ua72f\ua730\ua731\ua732\ua733\ua734\ua735\ua736\ua737\ua738\ua739\ua73a\ua73b\ua73c\ua73d\ua73e\ua73f\ua740\ua741\ua742\ua743\ua744\ua745\ua746\ua747\ua748\ua749\ua74a\ua74b\ua74c\ua74d\ua74e\ua74f\ua750\ua751\ua752\ua753\ua754\ua755\ua756\ua757\ua758\ua759\ua75a\ua75b\ua75c\ua75d\ua75e\ua75f\ua760\ua761\ua762\ua763\ua764\ua765\ua766\ua767\ua768\ua769\ua76a\ua76b\ua76c\ua76d\ua76e\ua76f\ua770\ua771\ua772\ua773\ua774\ua775\ua776\ua777\ua778\ua779\ua77a\ua77b\ua77c\ua77d\ua77e\ua77f\ua780\ua781\ua782\ua783\ua784\ua785\ua786\ua787\ua788\ua789\ua78a\ua78b\ua78c\ua78d\ua78e\ua78f\ua790\ua791\ua792\ua793\ua794\ua795\ua796\ua797\ua798\ua799\ua79a\ua79b\ua79c\ua79d\ua79e\ua79f\ua7a0\ua7a1\ua7a2\ua7a3\ua7a4\ua7a5\ua7a6\ua7a7\ua7a8\ua7a9\ua7aa\ua7ab\ua7ac\ua7ad\ua7ae\ua7af\ua7b0\ua7b1\ua7b2\ua7b3\ua7b4\ua7b5\ua7b6\ua7b7\ua7b8\ua7b9\ua7ba\ua7bb\ua7bc\ua7bd\ua7be\ua7bf\ua7c0\ua7c1\ua7c2\ua7c3\ua7c4\ua7c5\ua7c6\ua7c7\ua7c8\ua7c9\ua7ca\ua7cb\ua7cc\ua7cd\ua7ce\ua7cf\ua7d0\ua7d1\ua7d2\ua7d3\ua7d4\ua7d5\ua7d6\ua7d7\ua7d8\ua7d9\ua7da\ua7db\ua7dc\ua7dd\ua7de\ua7df\ua7e0\ua7e1\ua7e2\ua7e3\ua7e4\ua7e5\ua7e6\ua7e7\ua7e8\ua7e9\ua7ea\ua7eb\ua7ec\ua7ed\ua7ee\ua7ef\ua7f0\ua7f1\ua7f2\ua7f3\ua7f4\ua7f5\ua7f6\ua7f7\ua7f8\ua7f9\ua7fa\ua7fb\ua7fc\ua7fd\ua7fe\ua7ff\ua82c\ua82d\ua82e\ua82f\ua830\ua831\ua832\ua833\ua834\ua835\ua836\ua837\ua838\ua839\ua83a\ua83b\ua83c\ua83d\ua83e\ua83f\ua840\ua841\ua842\ua843\ua844\ua845\ua846\ua847\ua848\ua849\ua84a\ua84b\ua84c\ua84d\ua84e\ua84f\ua850\ua851\ua852\ua853\ua854\ua855\ua856\ua857\ua858\ua859\ua85a\ua85b\ua85c\ua85d\ua85e\ua85f\ua860\ua861\ua862\ua863\ua864\ua865\ua866\ua867\ua868\ua869\ua86a\ua86b\ua86c\ua86d\ua86e\ua86f\ua870\ua871\ua872\ua873\ua874\ua875\ua876\ua877\ua878\ua879\ua87a\ua87b\ua87c\ua87d\ua87e\ua87f\ua880\ua881\ua882\ua883\ua884\ua885\ua886\ua887\ua888\ua889\ua88a\ua88b\ua88c\ua88d\ua88e\ua88f\ua890\ua891\ua892\ua893\ua894\ua895\ua896\ua897\ua898\ua899\ua89a\ua89b\ua89c\ua89d\ua89e\ua89f\ua8a0\ua8a1\ua8a2\ua8a3\ua8a4\ua8a5\ua8a6\ua8a7\ua8a8\ua8a9\ua8aa\ua8ab\ua8ac\ua8ad\ua8ae\ua8af\ua8b0\ua8b1\ua8b2\ua8b3\ua8b4\ua8b5\ua8b6\ua8b7\ua8b8\ua8b9\ua8ba\ua8bb\ua8bc\ua8bd\ua8be\ua8bf\ua8c0\ua8c1\ua8c2\ua8c3\ua8c4\ua8c5\ua8c6\ua8c7\ua8c8\ua8c9\ua8ca\ua8cb\ua8cc\ua8cd\ua8ce\ua8cf\ua8d0\ua8d1\ua8d2\ua8d3\ua8d4\ua8d5\ua8d6\ua8d7\ua8d8\ua8d9\ua8da\ua8db\ua8dc\ua8dd\ua8de\ua8df\ua8e0\ua8e1\ua8e2\ua8e3\ua8e4\ua8e5\ua8e6\ua8e7\ua8e8\ua8e9\ua8ea\ua8eb\ua8ec\ua8ed\ua8ee\ua8ef\ua8f0\ua8f1\ua8f2\ua8f3\ua8f4\ua8f5\ua8f6\ua8f7\ua8f8\ua8f9\ua8fa\ua8fb\ua8fc\ua8fd\ua8fe\ua8ff\ua900\ua901\ua902\ua903\ua904\ua905\ua906\ua907\ua908\ua909\ua90a\ua90b\ua90c\ua90d\ua90e\ua90f\ua910\ua911\ua912\ua913\ua914\ua915\ua916\ua917\ua918\ua919\ua91a\ua91b\ua91c\ua91d\ua91e\ua91f\ua920\ua921\ua922\ua923\ua924\ua925\ua926\ua927\ua928\ua929\ua92a\ua92b\ua92c\ua92d\ua92e\ua92f\ua930\ua931\ua932\ua933\ua934\ua935\ua936\ua937\ua938\ua939\ua93a\ua93b\ua93c\ua93d\ua93e\ua93f\ua940\ua941\ua942\ua943\ua944\ua945\ua946\ua947\ua948\ua949\ua94a\ua94b\ua94c\ua94d\ua94e\ua94f\ua950\ua951\ua952\ua953\ua954\ua955\ua956\ua957\ua958\ua959\ua95a\ua95b\ua95c\ua95d\ua95e\ua95f\ua960\ua961\ua962\ua963\ua964\ua965\ua966\ua967\ua968\ua969\ua96a\ua96b\ua96c\ua96d\ua96e\ua96f\ua970\ua971\ua972\ua973\ua974\ua975\ua976\ua977\ua978\ua979\ua97a\ua97b\ua97c\ua97d\ua97e\ua97f\ua980\ua981\ua982\ua983\ua984\ua985\ua986\ua987\ua988\ua989\ua98a\ua98b\ua98c\ua98d\ua98e\ua98f\ua990\ua991\ua992\ua993\ua994\ua995\ua996\ua997\ua998\ua999\ua99a\ua99b\ua99c\ua99d\ua99e\ua99f\ua9a0\ua9a1\ua9a2\ua9a3\ua9a4\ua9a5\ua9a6\ua9a7\ua9a8\ua9a9\ua9aa\ua9ab\ua9ac\ua9ad\ua9ae\ua9af\ua9b0\ua9b1\ua9b2\ua9b3\ua9b4\ua9b5\ua9b6\ua9b7\ua9b8\ua9b9\ua9ba\ua9bb\ua9bc\ua9bd\ua9be\ua9bf\ua9c0\ua9c1\ua9c2\ua9c3\ua9c4\ua9c5\ua9c6\ua9c7\ua9c8\ua9c9\ua9ca\ua9cb\ua9cc\ua9cd\ua9ce\ua9cf\ua9d0\ua9d1\ua9d2\ua9d3\ua9d4\ua9d5\ua9d6\ua9d7\ua9d8\ua9d9\ua9da\ua9db\ua9dc\ua9dd\ua9de\ua9df\ua9e0\ua9e1\ua9e2\ua9e3\ua9e4\ua9e5\ua9e6\ua9e7\ua9e8\ua9e9\ua9ea\ua9eb\ua9ec\ua9ed\ua9ee\ua9ef\ua9f0\ua9f1\ua9f2\ua9f3\ua9f4\ua9f5\ua9f6\ua9f7\ua9f8\ua9f9\ua9fa\ua9fb\ua9fc\ua9fd\ua9fe\ua9ff\uaa00\uaa01\uaa02\uaa03\uaa04\uaa05\uaa06\uaa07\uaa08\uaa09\uaa0a\uaa0b\uaa0c\uaa0d\uaa0e\uaa0f\uaa10\uaa11\uaa12\uaa13\uaa14\uaa15\uaa16\uaa17\uaa18\uaa19\uaa1a\uaa1b\uaa1c\uaa1d\uaa1e\uaa1f\uaa20\uaa21\uaa22\uaa23\uaa24\uaa25\uaa26\uaa27\uaa28\uaa29\uaa2a\uaa2b\uaa2c\uaa2d\uaa2e\uaa2f\uaa30\uaa31\uaa32\uaa33\uaa34\uaa35\uaa36\uaa37\uaa38\uaa39\uaa3a\uaa3b\uaa3c\uaa3d\uaa3e\uaa3f\uaa40\uaa41\uaa42\uaa43\uaa44\uaa45\uaa46\uaa47\uaa48\uaa49\uaa4a\uaa4b\uaa4c\uaa4d\uaa4e\uaa4f\uaa50\uaa51\uaa52\uaa53\uaa54\uaa55\uaa56\uaa57\uaa58\uaa59\uaa5a\uaa5b\uaa5c\uaa5d\uaa5e\uaa5f\uaa60\uaa61\uaa62\uaa63\uaa64\uaa65\uaa66\uaa67\uaa68\uaa69\uaa6a\uaa6b\uaa6c\uaa6d\uaa6e\uaa6f\uaa70\uaa71\uaa72\uaa73\uaa74\uaa75\uaa76\uaa77\uaa78\uaa79\uaa7a\uaa7b\uaa7c\uaa7d\uaa7e\uaa7f\uaa80\uaa81\uaa82\uaa83\uaa84\uaa85\uaa86\uaa87\uaa88\uaa89\uaa8a\uaa8b\uaa8c\uaa8d\uaa8e\uaa8f\uaa90\uaa91\uaa92\uaa93\uaa94\uaa95\uaa96\uaa97\uaa98\uaa99\uaa9a\uaa9b\uaa9c\uaa9d\uaa9e\uaa9f\uaaa0\uaaa1\uaaa2\uaaa3\uaaa4\uaaa5\uaaa6\uaaa7\uaaa8\uaaa9\uaaaa\uaaab\uaaac\uaaad\uaaae\uaaaf\uaab0\uaab1\uaab2\uaab3\uaab4\uaab5\uaab6\uaab7\uaab8\uaab9\uaaba\uaabb\uaabc\uaabd\uaabe\uaabf\uaac0\uaac1\uaac2\uaac3\uaac4\uaac5\uaac6\uaac7\uaac8\uaac9\uaaca\uaacb\uaacc\uaacd\uaace\uaacf\uaad0\uaad1\uaad2\uaad3\uaad4\uaad5\uaad6\uaad7\uaad8\uaad9\uaada\uaadb\uaadc\uaadd\uaade\uaadf\uaae0\uaae1\uaae2\uaae3\uaae4\uaae5\uaae6\uaae7\uaae8\uaae9\uaaea\uaaeb\uaaec\uaaed\uaaee\uaaef\uaaf0\uaaf1\uaaf2\uaaf3\uaaf4\uaaf5\uaaf6\uaaf7\uaaf8\uaaf9\uaafa\uaafb\uaafc\uaafd\uaafe\uaaff\uab00\uab01\uab02\uab03\uab04\uab05\uab06\uab07\uab08\uab09\uab0a\uab0b\uab0c\uab0d\uab0e\uab0f\uab10\uab11\uab12\uab13\uab14\uab15\uab16\uab17\uab18\uab19\uab1a\uab1b\uab1c\uab1d\uab1e\uab1f\uab20\uab21\uab22\uab23\uab24\uab25\uab26\uab27\uab28\uab29\uab2a\uab2b\uab2c\uab2d\uab2e\uab2f\uab30\uab31\uab32\uab33\uab34\uab35\uab36\uab37\uab38\uab39\uab3a\uab3b\uab3c\uab3d\uab3e\uab3f\uab40\uab41\uab42\uab43\uab44\uab45\uab46\uab47\uab48\uab49\uab4a\uab4b\uab4c\uab4d\uab4e\uab4f\uab50\uab51\uab52\uab53\uab54\uab55\uab56\uab57\uab58\uab59\uab5a\uab5b\uab5c\uab5d\uab5e\uab5f\uab60\uab61\uab62\uab63\uab64\uab65\uab66\uab67\uab68\uab69\uab6a\uab6b\uab6c\uab6d\uab6e\uab6f\uab70\uab71\uab72\uab73\uab74\uab75\uab76\uab77\uab78\uab79\uab7a\uab7b\uab7c\uab7d\uab7e\uab7f\uab80\uab81\uab82\uab83\uab84\uab85\uab86\uab87\uab88\uab89\uab8a\uab8b\uab8c\uab8d\uab8e\uab8f\uab90\uab91\uab92\uab93\uab94\uab95\uab96\uab97\uab98\uab99\uab9a\uab9b\uab9c\uab9d\uab9e\uab9f\uaba0\uaba1\uaba2\uaba3\uaba4\uaba5\uaba6\uaba7\uaba8\uaba9\uabaa\uabab\uabac\uabad\uabae\uabaf\uabb0\uabb1\uabb2\uabb3\uabb4\uabb5\uabb6\uabb7\uabb8\uabb9\uabba\uabbb\uabbc\uabbd\uabbe\uabbf\uabc0\uabc1\uabc2\uabc3\uabc4\uabc5\uabc6\uabc7\uabc8\uabc9\uabca\uabcb\uabcc\uabcd\uabce\uabcf\uabd0\uabd1\uabd2\uabd3\uabd4\uabd5\uabd6\uabd7\uabd8\uabd9\uabda\uabdb\uabdc\uabdd\uabde\uabdf\uabe0\uabe1\uabe2\uabe3\uabe4\uabe5\uabe6\uabe7\uabe8\uabe9\uabea\uabeb\uabec\uabed\uabee\uabef\uabf0\uabf1\uabf2\uabf3\uabf4\uabf5\uabf6\uabf7\uabf8\uabf9\uabfa\uabfb\uabfc\uabfd\uabfe\uabff\ud7a4\ud7a5\ud7a6\ud7a7\ud7a8\ud7a9\ud7aa\ud7ab\ud7ac\ud7ad\ud7ae\ud7af\ud7b0\ud7b1\ud7b2\ud7b3\ud7b4\ud7b5\ud7b6\ud7b7\ud7b8\ud7b9\ud7ba\ud7bb\ud7bc\ud7bd\ud7be\ud7bf\ud7c0\ud7c1\ud7c2\ud7c3\ud7c4\ud7c5\ud7c6\ud7c7\ud7c8\ud7c9\ud7ca\ud7cb\ud7cc\ud7cd\ud7ce\ud7cf\ud7d0\ud7d1\ud7d2\ud7d3\ud7d4\ud7d5\ud7d6\ud7d7\ud7d8\ud7d9\ud7da\ud7db\ud7dc\ud7dd\ud7de\ud7df\ud7e0\ud7e1\ud7e2\ud7e3\ud7e4\ud7e5\ud7e6\ud7e7\ud7e8\ud7e9\ud7ea\ud7eb\ud7ec\ud7ed\ud7ee\ud7ef\ud7f0\ud7f1\ud7f2\ud7f3\ud7f4\ud7f5\ud7f6\ud7f7\ud7f8\ud7f9\ud7fa\ud7fb\ud7fc\ud7fd\ud7fe\ud7ff\ufa2e\ufa2f\ufa6b\ufa6c\ufa6d\ufa6e\ufa6f\ufada\ufadb\ufadc\ufadd\ufade\ufadf\ufae0\ufae1\ufae2\ufae3\ufae4\ufae5\ufae6\ufae7\ufae8\ufae9\ufaea\ufaeb\ufaec\ufaed\ufaee\ufaef\ufaf0\ufaf1\ufaf2\ufaf3\ufaf4\ufaf5\ufaf6\ufaf7\ufaf8\ufaf9\ufafa\ufafb\ufafc\ufafd\ufafe\ufaff\ufb07\ufb08\ufb09\ufb0a\ufb0b\ufb0c\ufb0d\ufb0e\ufb0f\ufb10\ufb11\ufb12\ufb18\ufb19\ufb1a\ufb1b\ufb1c\ufb37\ufb3d\ufb3f\ufb42\ufb45\ufbb2\ufbb3\ufbb4\ufbb5\ufbb6\ufbb7\ufbb8\ufbb9\ufbba\ufbbb\ufbbc\ufbbd\ufbbe\ufbbf\ufbc0\ufbc1\ufbc2\ufbc3\ufbc4\ufbc5\ufbc6\ufbc7\ufbc8\ufbc9\ufbca\ufbcb\ufbcc\ufbcd\ufbce\ufbcf\ufbd0\ufbd1\ufbd2\ufd40\ufd41\ufd42\ufd43\ufd44\ufd45\ufd46\ufd47\ufd48\ufd49\ufd4a\ufd4b\ufd4c\ufd4d\ufd4e\ufd4f\ufd90\ufd91\ufdc8\ufdc9\ufdca\ufdcb\ufdcc\ufdcd\ufdce\ufdcf\ufdd0\ufdd1\ufdd2\ufdd3\ufdd4\ufdd5\ufdd6\ufdd7\ufdd8\ufdd9\ufdda\ufddb\ufddc\ufddd\ufdde\ufddf\ufde0\ufde1\ufde2\ufde3\ufde4\ufde5\ufde6\ufde7\ufde8\ufde9\ufdea\ufdeb\ufdec\ufded\ufdee\ufdef\ufdfe\ufdff\ufe1a\ufe1b\ufe1c\ufe1d\ufe1e\ufe1f\ufe24\ufe25\ufe26\ufe27\ufe28\ufe29\ufe2a\ufe2b\ufe2c\ufe2d\ufe2e\ufe2f\ufe53\ufe67\ufe6c\ufe6d\ufe6e\ufe6f\ufe75\ufefd\ufefe\uff00\uffbf\uffc0\uffc1\uffc8\uffc9\uffd0\uffd1\uffd8\uffd9\uffdd\uffde\uffdf\uffe7\uffef\ufff0\ufff1\ufff2\ufff3\ufff4\ufff5\ufff6\ufff7\ufff8\ufffe' + +Co = u'\ue000\ue001\ue002\ue003\ue004\ue005\ue006\ue007\ue008\ue009\ue00a\ue00b\ue00c\ue00d\ue00e\ue00f\ue010\ue011\ue012\ue013\ue014\ue015\ue016\ue017\ue018\ue019\ue01a\ue01b\ue01c\ue01d\ue01e\ue01f\ue020\ue021\ue022\ue023\ue024\ue025\ue026\ue027\ue028\ue029\ue02a\ue02b\ue02c\ue02d\ue02e\ue02f\ue030\ue031\ue032\ue033\ue034\ue035\ue036\ue037\ue038\ue039\ue03a\ue03b\ue03c\ue03d\ue03e\ue03f\ue040\ue041\ue042\ue043\ue044\ue045\ue046\ue047\ue048\ue049\ue04a\ue04b\ue04c\ue04d\ue04e\ue04f\ue050\ue051\ue052\ue053\ue054\ue055\ue056\ue057\ue058\ue059\ue05a\ue05b\ue05c\ue05d\ue05e\ue05f\ue060\ue061\ue062\ue063\ue064\ue065\ue066\ue067\ue068\ue069\ue06a\ue06b\ue06c\ue06d\ue06e\ue06f\ue070\ue071\ue072\ue073\ue074\ue075\ue076\ue077\ue078\ue079\ue07a\ue07b\ue07c\ue07d\ue07e\ue07f\ue080\ue081\ue082\ue083\ue084\ue085\ue086\ue087\ue088\ue089\ue08a\ue08b\ue08c\ue08d\ue08e\ue08f\ue090\ue091\ue092\ue093\ue094\ue095\ue096\ue097\ue098\ue099\ue09a\ue09b\ue09c\ue09d\ue09e\ue09f\ue0a0\ue0a1\ue0a2\ue0a3\ue0a4\ue0a5\ue0a6\ue0a7\ue0a8\ue0a9\ue0aa\ue0ab\ue0ac\ue0ad\ue0ae\ue0af\ue0b0\ue0b1\ue0b2\ue0b3\ue0b4\ue0b5\ue0b6\ue0b7\ue0b8\ue0b9\ue0ba\ue0bb\ue0bc\ue0bd\ue0be\ue0bf\ue0c0\ue0c1\ue0c2\ue0c3\ue0c4\ue0c5\ue0c6\ue0c7\ue0c8\ue0c9\ue0ca\ue0cb\ue0cc\ue0cd\ue0ce\ue0cf\ue0d0\ue0d1\ue0d2\ue0d3\ue0d4\ue0d5\ue0d6\ue0d7\ue0d8\ue0d9\ue0da\ue0db\ue0dc\ue0dd\ue0de\ue0df\ue0e0\ue0e1\ue0e2\ue0e3\ue0e4\ue0e5\ue0e6\ue0e7\ue0e8\ue0e9\ue0ea\ue0eb\ue0ec\ue0ed\ue0ee\ue0ef\ue0f0\ue0f1\ue0f2\ue0f3\ue0f4\ue0f5\ue0f6\ue0f7\ue0f8\ue0f9\ue0fa\ue0fb\ue0fc\ue0fd\ue0fe\ue0ff\ue100\ue101\ue102\ue103\ue104\ue105\ue106\ue107\ue108\ue109\ue10a\ue10b\ue10c\ue10d\ue10e\ue10f\ue110\ue111\ue112\ue113\ue114\ue115\ue116\ue117\ue118\ue119\ue11a\ue11b\ue11c\ue11d\ue11e\ue11f\ue120\ue121\ue122\ue123\ue124\ue125\ue126\ue127\ue128\ue129\ue12a\ue12b\ue12c\ue12d\ue12e\ue12f\ue130\ue131\ue132\ue133\ue134\ue135\ue136\ue137\ue138\ue139\ue13a\ue13b\ue13c\ue13d\ue13e\ue13f\ue140\ue141\ue142\ue143\ue144\ue145\ue146\ue147\ue148\ue149\ue14a\ue14b\ue14c\ue14d\ue14e\ue14f\ue150\ue151\ue152\ue153\ue154\ue155\ue156\ue157\ue158\ue159\ue15a\ue15b\ue15c\ue15d\ue15e\ue15f\ue160\ue161\ue162\ue163\ue164\ue165\ue166\ue167\ue168\ue169\ue16a\ue16b\ue16c\ue16d\ue16e\ue16f\ue170\ue171\ue172\ue173\ue174\ue175\ue176\ue177\ue178\ue179\ue17a\ue17b\ue17c\ue17d\ue17e\ue17f\ue180\ue181\ue182\ue183\ue184\ue185\ue186\ue187\ue188\ue189\ue18a\ue18b\ue18c\ue18d\ue18e\ue18f\ue190\ue191\ue192\ue193\ue194\ue195\ue196\ue197\ue198\ue199\ue19a\ue19b\ue19c\ue19d\ue19e\ue19f\ue1a0\ue1a1\ue1a2\ue1a3\ue1a4\ue1a5\ue1a6\ue1a7\ue1a8\ue1a9\ue1aa\ue1ab\ue1ac\ue1ad\ue1ae\ue1af\ue1b0\ue1b1\ue1b2\ue1b3\ue1b4\ue1b5\ue1b6\ue1b7\ue1b8\ue1b9\ue1ba\ue1bb\ue1bc\ue1bd\ue1be\ue1bf\ue1c0\ue1c1\ue1c2\ue1c3\ue1c4\ue1c5\ue1c6\ue1c7\ue1c8\ue1c9\ue1ca\ue1cb\ue1cc\ue1cd\ue1ce\ue1cf\ue1d0\ue1d1\ue1d2\ue1d3\ue1d4\ue1d5\ue1d6\ue1d7\ue1d8\ue1d9\ue1da\ue1db\ue1dc\ue1dd\ue1de\ue1df\ue1e0\ue1e1\ue1e2\ue1e3\ue1e4\ue1e5\ue1e6\ue1e7\ue1e8\ue1e9\ue1ea\ue1eb\ue1ec\ue1ed\ue1ee\ue1ef\ue1f0\ue1f1\ue1f2\ue1f3\ue1f4\ue1f5\ue1f6\ue1f7\ue1f8\ue1f9\ue1fa\ue1fb\ue1fc\ue1fd\ue1fe\ue1ff\ue200\ue201\ue202\ue203\ue204\ue205\ue206\ue207\ue208\ue209\ue20a\ue20b\ue20c\ue20d\ue20e\ue20f\ue210\ue211\ue212\ue213\ue214\ue215\ue216\ue217\ue218\ue219\ue21a\ue21b\ue21c\ue21d\ue21e\ue21f\ue220\ue221\ue222\ue223\ue224\ue225\ue226\ue227\ue228\ue229\ue22a\ue22b\ue22c\ue22d\ue22e\ue22f\ue230\ue231\ue232\ue233\ue234\ue235\ue236\ue237\ue238\ue239\ue23a\ue23b\ue23c\ue23d\ue23e\ue23f\ue240\ue241\ue242\ue243\ue244\ue245\ue246\ue247\ue248\ue249\ue24a\ue24b\ue24c\ue24d\ue24e\ue24f\ue250\ue251\ue252\ue253\ue254\ue255\ue256\ue257\ue258\ue259\ue25a\ue25b\ue25c\ue25d\ue25e\ue25f\ue260\ue261\ue262\ue263\ue264\ue265\ue266\ue267\ue268\ue269\ue26a\ue26b\ue26c\ue26d\ue26e\ue26f\ue270\ue271\ue272\ue273\ue274\ue275\ue276\ue277\ue278\ue279\ue27a\ue27b\ue27c\ue27d\ue27e\ue27f\ue280\ue281\ue282\ue283\ue284\ue285\ue286\ue287\ue288\ue289\ue28a\ue28b\ue28c\ue28d\ue28e\ue28f\ue290\ue291\ue292\ue293\ue294\ue295\ue296\ue297\ue298\ue299\ue29a\ue29b\ue29c\ue29d\ue29e\ue29f\ue2a0\ue2a1\ue2a2\ue2a3\ue2a4\ue2a5\ue2a6\ue2a7\ue2a8\ue2a9\ue2aa\ue2ab\ue2ac\ue2ad\ue2ae\ue2af\ue2b0\ue2b1\ue2b2\ue2b3\ue2b4\ue2b5\ue2b6\ue2b7\ue2b8\ue2b9\ue2ba\ue2bb\ue2bc\ue2bd\ue2be\ue2bf\ue2c0\ue2c1\ue2c2\ue2c3\ue2c4\ue2c5\ue2c6\ue2c7\ue2c8\ue2c9\ue2ca\ue2cb\ue2cc\ue2cd\ue2ce\ue2cf\ue2d0\ue2d1\ue2d2\ue2d3\ue2d4\ue2d5\ue2d6\ue2d7\ue2d8\ue2d9\ue2da\ue2db\ue2dc\ue2dd\ue2de\ue2df\ue2e0\ue2e1\ue2e2\ue2e3\ue2e4\ue2e5\ue2e6\ue2e7\ue2e8\ue2e9\ue2ea\ue2eb\ue2ec\ue2ed\ue2ee\ue2ef\ue2f0\ue2f1\ue2f2\ue2f3\ue2f4\ue2f5\ue2f6\ue2f7\ue2f8\ue2f9\ue2fa\ue2fb\ue2fc\ue2fd\ue2fe\ue2ff\ue300\ue301\ue302\ue303\ue304\ue305\ue306\ue307\ue308\ue309\ue30a\ue30b\ue30c\ue30d\ue30e\ue30f\ue310\ue311\ue312\ue313\ue314\ue315\ue316\ue317\ue318\ue319\ue31a\ue31b\ue31c\ue31d\ue31e\ue31f\ue320\ue321\ue322\ue323\ue324\ue325\ue326\ue327\ue328\ue329\ue32a\ue32b\ue32c\ue32d\ue32e\ue32f\ue330\ue331\ue332\ue333\ue334\ue335\ue336\ue337\ue338\ue339\ue33a\ue33b\ue33c\ue33d\ue33e\ue33f\ue340\ue341\ue342\ue343\ue344\ue345\ue346\ue347\ue348\ue349\ue34a\ue34b\ue34c\ue34d\ue34e\ue34f\ue350\ue351\ue352\ue353\ue354\ue355\ue356\ue357\ue358\ue359\ue35a\ue35b\ue35c\ue35d\ue35e\ue35f\ue360\ue361\ue362\ue363\ue364\ue365\ue366\ue367\ue368\ue369\ue36a\ue36b\ue36c\ue36d\ue36e\ue36f\ue370\ue371\ue372\ue373\ue374\ue375\ue376\ue377\ue378\ue379\ue37a\ue37b\ue37c\ue37d\ue37e\ue37f\ue380\ue381\ue382\ue383\ue384\ue385\ue386\ue387\ue388\ue389\ue38a\ue38b\ue38c\ue38d\ue38e\ue38f\ue390\ue391\ue392\ue393\ue394\ue395\ue396\ue397\ue398\ue399\ue39a\ue39b\ue39c\ue39d\ue39e\ue39f\ue3a0\ue3a1\ue3a2\ue3a3\ue3a4\ue3a5\ue3a6\ue3a7\ue3a8\ue3a9\ue3aa\ue3ab\ue3ac\ue3ad\ue3ae\ue3af\ue3b0\ue3b1\ue3b2\ue3b3\ue3b4\ue3b5\ue3b6\ue3b7\ue3b8\ue3b9\ue3ba\ue3bb\ue3bc\ue3bd\ue3be\ue3bf\ue3c0\ue3c1\ue3c2\ue3c3\ue3c4\ue3c5\ue3c6\ue3c7\ue3c8\ue3c9\ue3ca\ue3cb\ue3cc\ue3cd\ue3ce\ue3cf\ue3d0\ue3d1\ue3d2\ue3d3\ue3d4\ue3d5\ue3d6\ue3d7\ue3d8\ue3d9\ue3da\ue3db\ue3dc\ue3dd\ue3de\ue3df\ue3e0\ue3e1\ue3e2\ue3e3\ue3e4\ue3e5\ue3e6\ue3e7\ue3e8\ue3e9\ue3ea\ue3eb\ue3ec\ue3ed\ue3ee\ue3ef\ue3f0\ue3f1\ue3f2\ue3f3\ue3f4\ue3f5\ue3f6\ue3f7\ue3f8\ue3f9\ue3fa\ue3fb\ue3fc\ue3fd\ue3fe\ue3ff\ue400\ue401\ue402\ue403\ue404\ue405\ue406\ue407\ue408\ue409\ue40a\ue40b\ue40c\ue40d\ue40e\ue40f\ue410\ue411\ue412\ue413\ue414\ue415\ue416\ue417\ue418\ue419\ue41a\ue41b\ue41c\ue41d\ue41e\ue41f\ue420\ue421\ue422\ue423\ue424\ue425\ue426\ue427\ue428\ue429\ue42a\ue42b\ue42c\ue42d\ue42e\ue42f\ue430\ue431\ue432\ue433\ue434\ue435\ue436\ue437\ue438\ue439\ue43a\ue43b\ue43c\ue43d\ue43e\ue43f\ue440\ue441\ue442\ue443\ue444\ue445\ue446\ue447\ue448\ue449\ue44a\ue44b\ue44c\ue44d\ue44e\ue44f\ue450\ue451\ue452\ue453\ue454\ue455\ue456\ue457\ue458\ue459\ue45a\ue45b\ue45c\ue45d\ue45e\ue45f\ue460\ue461\ue462\ue463\ue464\ue465\ue466\ue467\ue468\ue469\ue46a\ue46b\ue46c\ue46d\ue46e\ue46f\ue470\ue471\ue472\ue473\ue474\ue475\ue476\ue477\ue478\ue479\ue47a\ue47b\ue47c\ue47d\ue47e\ue47f\ue480\ue481\ue482\ue483\ue484\ue485\ue486\ue487\ue488\ue489\ue48a\ue48b\ue48c\ue48d\ue48e\ue48f\ue490\ue491\ue492\ue493\ue494\ue495\ue496\ue497\ue498\ue499\ue49a\ue49b\ue49c\ue49d\ue49e\ue49f\ue4a0\ue4a1\ue4a2\ue4a3\ue4a4\ue4a5\ue4a6\ue4a7\ue4a8\ue4a9\ue4aa\ue4ab\ue4ac\ue4ad\ue4ae\ue4af\ue4b0\ue4b1\ue4b2\ue4b3\ue4b4\ue4b5\ue4b6\ue4b7\ue4b8\ue4b9\ue4ba\ue4bb\ue4bc\ue4bd\ue4be\ue4bf\ue4c0\ue4c1\ue4c2\ue4c3\ue4c4\ue4c5\ue4c6\ue4c7\ue4c8\ue4c9\ue4ca\ue4cb\ue4cc\ue4cd\ue4ce\ue4cf\ue4d0\ue4d1\ue4d2\ue4d3\ue4d4\ue4d5\ue4d6\ue4d7\ue4d8\ue4d9\ue4da\ue4db\ue4dc\ue4dd\ue4de\ue4df\ue4e0\ue4e1\ue4e2\ue4e3\ue4e4\ue4e5\ue4e6\ue4e7\ue4e8\ue4e9\ue4ea\ue4eb\ue4ec\ue4ed\ue4ee\ue4ef\ue4f0\ue4f1\ue4f2\ue4f3\ue4f4\ue4f5\ue4f6\ue4f7\ue4f8\ue4f9\ue4fa\ue4fb\ue4fc\ue4fd\ue4fe\ue4ff\ue500\ue501\ue502\ue503\ue504\ue505\ue506\ue507\ue508\ue509\ue50a\ue50b\ue50c\ue50d\ue50e\ue50f\ue510\ue511\ue512\ue513\ue514\ue515\ue516\ue517\ue518\ue519\ue51a\ue51b\ue51c\ue51d\ue51e\ue51f\ue520\ue521\ue522\ue523\ue524\ue525\ue526\ue527\ue528\ue529\ue52a\ue52b\ue52c\ue52d\ue52e\ue52f\ue530\ue531\ue532\ue533\ue534\ue535\ue536\ue537\ue538\ue539\ue53a\ue53b\ue53c\ue53d\ue53e\ue53f\ue540\ue541\ue542\ue543\ue544\ue545\ue546\ue547\ue548\ue549\ue54a\ue54b\ue54c\ue54d\ue54e\ue54f\ue550\ue551\ue552\ue553\ue554\ue555\ue556\ue557\ue558\ue559\ue55a\ue55b\ue55c\ue55d\ue55e\ue55f\ue560\ue561\ue562\ue563\ue564\ue565\ue566\ue567\ue568\ue569\ue56a\ue56b\ue56c\ue56d\ue56e\ue56f\ue570\ue571\ue572\ue573\ue574\ue575\ue576\ue577\ue578\ue579\ue57a\ue57b\ue57c\ue57d\ue57e\ue57f\ue580\ue581\ue582\ue583\ue584\ue585\ue586\ue587\ue588\ue589\ue58a\ue58b\ue58c\ue58d\ue58e\ue58f\ue590\ue591\ue592\ue593\ue594\ue595\ue596\ue597\ue598\ue599\ue59a\ue59b\ue59c\ue59d\ue59e\ue59f\ue5a0\ue5a1\ue5a2\ue5a3\ue5a4\ue5a5\ue5a6\ue5a7\ue5a8\ue5a9\ue5aa\ue5ab\ue5ac\ue5ad\ue5ae\ue5af\ue5b0\ue5b1\ue5b2\ue5b3\ue5b4\ue5b5\ue5b6\ue5b7\ue5b8\ue5b9\ue5ba\ue5bb\ue5bc\ue5bd\ue5be\ue5bf\ue5c0\ue5c1\ue5c2\ue5c3\ue5c4\ue5c5\ue5c6\ue5c7\ue5c8\ue5c9\ue5ca\ue5cb\ue5cc\ue5cd\ue5ce\ue5cf\ue5d0\ue5d1\ue5d2\ue5d3\ue5d4\ue5d5\ue5d6\ue5d7\ue5d8\ue5d9\ue5da\ue5db\ue5dc\ue5dd\ue5de\ue5df\ue5e0\ue5e1\ue5e2\ue5e3\ue5e4\ue5e5\ue5e6\ue5e7\ue5e8\ue5e9\ue5ea\ue5eb\ue5ec\ue5ed\ue5ee\ue5ef\ue5f0\ue5f1\ue5f2\ue5f3\ue5f4\ue5f5\ue5f6\ue5f7\ue5f8\ue5f9\ue5fa\ue5fb\ue5fc\ue5fd\ue5fe\ue5ff\ue600\ue601\ue602\ue603\ue604\ue605\ue606\ue607\ue608\ue609\ue60a\ue60b\ue60c\ue60d\ue60e\ue60f\ue610\ue611\ue612\ue613\ue614\ue615\ue616\ue617\ue618\ue619\ue61a\ue61b\ue61c\ue61d\ue61e\ue61f\ue620\ue621\ue622\ue623\ue624\ue625\ue626\ue627\ue628\ue629\ue62a\ue62b\ue62c\ue62d\ue62e\ue62f\ue630\ue631\ue632\ue633\ue634\ue635\ue636\ue637\ue638\ue639\ue63a\ue63b\ue63c\ue63d\ue63e\ue63f\ue640\ue641\ue642\ue643\ue644\ue645\ue646\ue647\ue648\ue649\ue64a\ue64b\ue64c\ue64d\ue64e\ue64f\ue650\ue651\ue652\ue653\ue654\ue655\ue656\ue657\ue658\ue659\ue65a\ue65b\ue65c\ue65d\ue65e\ue65f\ue660\ue661\ue662\ue663\ue664\ue665\ue666\ue667\ue668\ue669\ue66a\ue66b\ue66c\ue66d\ue66e\ue66f\ue670\ue671\ue672\ue673\ue674\ue675\ue676\ue677\ue678\ue679\ue67a\ue67b\ue67c\ue67d\ue67e\ue67f\ue680\ue681\ue682\ue683\ue684\ue685\ue686\ue687\ue688\ue689\ue68a\ue68b\ue68c\ue68d\ue68e\ue68f\ue690\ue691\ue692\ue693\ue694\ue695\ue696\ue697\ue698\ue699\ue69a\ue69b\ue69c\ue69d\ue69e\ue69f\ue6a0\ue6a1\ue6a2\ue6a3\ue6a4\ue6a5\ue6a6\ue6a7\ue6a8\ue6a9\ue6aa\ue6ab\ue6ac\ue6ad\ue6ae\ue6af\ue6b0\ue6b1\ue6b2\ue6b3\ue6b4\ue6b5\ue6b6\ue6b7\ue6b8\ue6b9\ue6ba\ue6bb\ue6bc\ue6bd\ue6be\ue6bf\ue6c0\ue6c1\ue6c2\ue6c3\ue6c4\ue6c5\ue6c6\ue6c7\ue6c8\ue6c9\ue6ca\ue6cb\ue6cc\ue6cd\ue6ce\ue6cf\ue6d0\ue6d1\ue6d2\ue6d3\ue6d4\ue6d5\ue6d6\ue6d7\ue6d8\ue6d9\ue6da\ue6db\ue6dc\ue6dd\ue6de\ue6df\ue6e0\ue6e1\ue6e2\ue6e3\ue6e4\ue6e5\ue6e6\ue6e7\ue6e8\ue6e9\ue6ea\ue6eb\ue6ec\ue6ed\ue6ee\ue6ef\ue6f0\ue6f1\ue6f2\ue6f3\ue6f4\ue6f5\ue6f6\ue6f7\ue6f8\ue6f9\ue6fa\ue6fb\ue6fc\ue6fd\ue6fe\ue6ff\ue700\ue701\ue702\ue703\ue704\ue705\ue706\ue707\ue708\ue709\ue70a\ue70b\ue70c\ue70d\ue70e\ue70f\ue710\ue711\ue712\ue713\ue714\ue715\ue716\ue717\ue718\ue719\ue71a\ue71b\ue71c\ue71d\ue71e\ue71f\ue720\ue721\ue722\ue723\ue724\ue725\ue726\ue727\ue728\ue729\ue72a\ue72b\ue72c\ue72d\ue72e\ue72f\ue730\ue731\ue732\ue733\ue734\ue735\ue736\ue737\ue738\ue739\ue73a\ue73b\ue73c\ue73d\ue73e\ue73f\ue740\ue741\ue742\ue743\ue744\ue745\ue746\ue747\ue748\ue749\ue74a\ue74b\ue74c\ue74d\ue74e\ue74f\ue750\ue751\ue752\ue753\ue754\ue755\ue756\ue757\ue758\ue759\ue75a\ue75b\ue75c\ue75d\ue75e\ue75f\ue760\ue761\ue762\ue763\ue764\ue765\ue766\ue767\ue768\ue769\ue76a\ue76b\ue76c\ue76d\ue76e\ue76f\ue770\ue771\ue772\ue773\ue774\ue775\ue776\ue777\ue778\ue779\ue77a\ue77b\ue77c\ue77d\ue77e\ue77f\ue780\ue781\ue782\ue783\ue784\ue785\ue786\ue787\ue788\ue789\ue78a\ue78b\ue78c\ue78d\ue78e\ue78f\ue790\ue791\ue792\ue793\ue794\ue795\ue796\ue797\ue798\ue799\ue79a\ue79b\ue79c\ue79d\ue79e\ue79f\ue7a0\ue7a1\ue7a2\ue7a3\ue7a4\ue7a5\ue7a6\ue7a7\ue7a8\ue7a9\ue7aa\ue7ab\ue7ac\ue7ad\ue7ae\ue7af\ue7b0\ue7b1\ue7b2\ue7b3\ue7b4\ue7b5\ue7b6\ue7b7\ue7b8\ue7b9\ue7ba\ue7bb\ue7bc\ue7bd\ue7be\ue7bf\ue7c0\ue7c1\ue7c2\ue7c3\ue7c4\ue7c5\ue7c6\ue7c7\ue7c8\ue7c9\ue7ca\ue7cb\ue7cc\ue7cd\ue7ce\ue7cf\ue7d0\ue7d1\ue7d2\ue7d3\ue7d4\ue7d5\ue7d6\ue7d7\ue7d8\ue7d9\ue7da\ue7db\ue7dc\ue7dd\ue7de\ue7df\ue7e0\ue7e1\ue7e2\ue7e3\ue7e4\ue7e5\ue7e6\ue7e7\ue7e8\ue7e9\ue7ea\ue7eb\ue7ec\ue7ed\ue7ee\ue7ef\ue7f0\ue7f1\ue7f2\ue7f3\ue7f4\ue7f5\ue7f6\ue7f7\ue7f8\ue7f9\ue7fa\ue7fb\ue7fc\ue7fd\ue7fe\ue7ff\ue800\ue801\ue802\ue803\ue804\ue805\ue806\ue807\ue808\ue809\ue80a\ue80b\ue80c\ue80d\ue80e\ue80f\ue810\ue811\ue812\ue813\ue814\ue815\ue816\ue817\ue818\ue819\ue81a\ue81b\ue81c\ue81d\ue81e\ue81f\ue820\ue821\ue822\ue823\ue824\ue825\ue826\ue827\ue828\ue829\ue82a\ue82b\ue82c\ue82d\ue82e\ue82f\ue830\ue831\ue832\ue833\ue834\ue835\ue836\ue837\ue838\ue839\ue83a\ue83b\ue83c\ue83d\ue83e\ue83f\ue840\ue841\ue842\ue843\ue844\ue845\ue846\ue847\ue848\ue849\ue84a\ue84b\ue84c\ue84d\ue84e\ue84f\ue850\ue851\ue852\ue853\ue854\ue855\ue856\ue857\ue858\ue859\ue85a\ue85b\ue85c\ue85d\ue85e\ue85f\ue860\ue861\ue862\ue863\ue864\ue865\ue866\ue867\ue868\ue869\ue86a\ue86b\ue86c\ue86d\ue86e\ue86f\ue870\ue871\ue872\ue873\ue874\ue875\ue876\ue877\ue878\ue879\ue87a\ue87b\ue87c\ue87d\ue87e\ue87f\ue880\ue881\ue882\ue883\ue884\ue885\ue886\ue887\ue888\ue889\ue88a\ue88b\ue88c\ue88d\ue88e\ue88f\ue890\ue891\ue892\ue893\ue894\ue895\ue896\ue897\ue898\ue899\ue89a\ue89b\ue89c\ue89d\ue89e\ue89f\ue8a0\ue8a1\ue8a2\ue8a3\ue8a4\ue8a5\ue8a6\ue8a7\ue8a8\ue8a9\ue8aa\ue8ab\ue8ac\ue8ad\ue8ae\ue8af\ue8b0\ue8b1\ue8b2\ue8b3\ue8b4\ue8b5\ue8b6\ue8b7\ue8b8\ue8b9\ue8ba\ue8bb\ue8bc\ue8bd\ue8be\ue8bf\ue8c0\ue8c1\ue8c2\ue8c3\ue8c4\ue8c5\ue8c6\ue8c7\ue8c8\ue8c9\ue8ca\ue8cb\ue8cc\ue8cd\ue8ce\ue8cf\ue8d0\ue8d1\ue8d2\ue8d3\ue8d4\ue8d5\ue8d6\ue8d7\ue8d8\ue8d9\ue8da\ue8db\ue8dc\ue8dd\ue8de\ue8df\ue8e0\ue8e1\ue8e2\ue8e3\ue8e4\ue8e5\ue8e6\ue8e7\ue8e8\ue8e9\ue8ea\ue8eb\ue8ec\ue8ed\ue8ee\ue8ef\ue8f0\ue8f1\ue8f2\ue8f3\ue8f4\ue8f5\ue8f6\ue8f7\ue8f8\ue8f9\ue8fa\ue8fb\ue8fc\ue8fd\ue8fe\ue8ff\ue900\ue901\ue902\ue903\ue904\ue905\ue906\ue907\ue908\ue909\ue90a\ue90b\ue90c\ue90d\ue90e\ue90f\ue910\ue911\ue912\ue913\ue914\ue915\ue916\ue917\ue918\ue919\ue91a\ue91b\ue91c\ue91d\ue91e\ue91f\ue920\ue921\ue922\ue923\ue924\ue925\ue926\ue927\ue928\ue929\ue92a\ue92b\ue92c\ue92d\ue92e\ue92f\ue930\ue931\ue932\ue933\ue934\ue935\ue936\ue937\ue938\ue939\ue93a\ue93b\ue93c\ue93d\ue93e\ue93f\ue940\ue941\ue942\ue943\ue944\ue945\ue946\ue947\ue948\ue949\ue94a\ue94b\ue94c\ue94d\ue94e\ue94f\ue950\ue951\ue952\ue953\ue954\ue955\ue956\ue957\ue958\ue959\ue95a\ue95b\ue95c\ue95d\ue95e\ue95f\ue960\ue961\ue962\ue963\ue964\ue965\ue966\ue967\ue968\ue969\ue96a\ue96b\ue96c\ue96d\ue96e\ue96f\ue970\ue971\ue972\ue973\ue974\ue975\ue976\ue977\ue978\ue979\ue97a\ue97b\ue97c\ue97d\ue97e\ue97f\ue980\ue981\ue982\ue983\ue984\ue985\ue986\ue987\ue988\ue989\ue98a\ue98b\ue98c\ue98d\ue98e\ue98f\ue990\ue991\ue992\ue993\ue994\ue995\ue996\ue997\ue998\ue999\ue99a\ue99b\ue99c\ue99d\ue99e\ue99f\ue9a0\ue9a1\ue9a2\ue9a3\ue9a4\ue9a5\ue9a6\ue9a7\ue9a8\ue9a9\ue9aa\ue9ab\ue9ac\ue9ad\ue9ae\ue9af\ue9b0\ue9b1\ue9b2\ue9b3\ue9b4\ue9b5\ue9b6\ue9b7\ue9b8\ue9b9\ue9ba\ue9bb\ue9bc\ue9bd\ue9be\ue9bf\ue9c0\ue9c1\ue9c2\ue9c3\ue9c4\ue9c5\ue9c6\ue9c7\ue9c8\ue9c9\ue9ca\ue9cb\ue9cc\ue9cd\ue9ce\ue9cf\ue9d0\ue9d1\ue9d2\ue9d3\ue9d4\ue9d5\ue9d6\ue9d7\ue9d8\ue9d9\ue9da\ue9db\ue9dc\ue9dd\ue9de\ue9df\ue9e0\ue9e1\ue9e2\ue9e3\ue9e4\ue9e5\ue9e6\ue9e7\ue9e8\ue9e9\ue9ea\ue9eb\ue9ec\ue9ed\ue9ee\ue9ef\ue9f0\ue9f1\ue9f2\ue9f3\ue9f4\ue9f5\ue9f6\ue9f7\ue9f8\ue9f9\ue9fa\ue9fb\ue9fc\ue9fd\ue9fe\ue9ff\uea00\uea01\uea02\uea03\uea04\uea05\uea06\uea07\uea08\uea09\uea0a\uea0b\uea0c\uea0d\uea0e\uea0f\uea10\uea11\uea12\uea13\uea14\uea15\uea16\uea17\uea18\uea19\uea1a\uea1b\uea1c\uea1d\uea1e\uea1f\uea20\uea21\uea22\uea23\uea24\uea25\uea26\uea27\uea28\uea29\uea2a\uea2b\uea2c\uea2d\uea2e\uea2f\uea30\uea31\uea32\uea33\uea34\uea35\uea36\uea37\uea38\uea39\uea3a\uea3b\uea3c\uea3d\uea3e\uea3f\uea40\uea41\uea42\uea43\uea44\uea45\uea46\uea47\uea48\uea49\uea4a\uea4b\uea4c\uea4d\uea4e\uea4f\uea50\uea51\uea52\uea53\uea54\uea55\uea56\uea57\uea58\uea59\uea5a\uea5b\uea5c\uea5d\uea5e\uea5f\uea60\uea61\uea62\uea63\uea64\uea65\uea66\uea67\uea68\uea69\uea6a\uea6b\uea6c\uea6d\uea6e\uea6f\uea70\uea71\uea72\uea73\uea74\uea75\uea76\uea77\uea78\uea79\uea7a\uea7b\uea7c\uea7d\uea7e\uea7f\uea80\uea81\uea82\uea83\uea84\uea85\uea86\uea87\uea88\uea89\uea8a\uea8b\uea8c\uea8d\uea8e\uea8f\uea90\uea91\uea92\uea93\uea94\uea95\uea96\uea97\uea98\uea99\uea9a\uea9b\uea9c\uea9d\uea9e\uea9f\ueaa0\ueaa1\ueaa2\ueaa3\ueaa4\ueaa5\ueaa6\ueaa7\ueaa8\ueaa9\ueaaa\ueaab\ueaac\ueaad\ueaae\ueaaf\ueab0\ueab1\ueab2\ueab3\ueab4\ueab5\ueab6\ueab7\ueab8\ueab9\ueaba\ueabb\ueabc\ueabd\ueabe\ueabf\ueac0\ueac1\ueac2\ueac3\ueac4\ueac5\ueac6\ueac7\ueac8\ueac9\ueaca\ueacb\ueacc\ueacd\ueace\ueacf\uead0\uead1\uead2\uead3\uead4\uead5\uead6\uead7\uead8\uead9\ueada\ueadb\ueadc\ueadd\ueade\ueadf\ueae0\ueae1\ueae2\ueae3\ueae4\ueae5\ueae6\ueae7\ueae8\ueae9\ueaea\ueaeb\ueaec\ueaed\ueaee\ueaef\ueaf0\ueaf1\ueaf2\ueaf3\ueaf4\ueaf5\ueaf6\ueaf7\ueaf8\ueaf9\ueafa\ueafb\ueafc\ueafd\ueafe\ueaff\ueb00\ueb01\ueb02\ueb03\ueb04\ueb05\ueb06\ueb07\ueb08\ueb09\ueb0a\ueb0b\ueb0c\ueb0d\ueb0e\ueb0f\ueb10\ueb11\ueb12\ueb13\ueb14\ueb15\ueb16\ueb17\ueb18\ueb19\ueb1a\ueb1b\ueb1c\ueb1d\ueb1e\ueb1f\ueb20\ueb21\ueb22\ueb23\ueb24\ueb25\ueb26\ueb27\ueb28\ueb29\ueb2a\ueb2b\ueb2c\ueb2d\ueb2e\ueb2f\ueb30\ueb31\ueb32\ueb33\ueb34\ueb35\ueb36\ueb37\ueb38\ueb39\ueb3a\ueb3b\ueb3c\ueb3d\ueb3e\ueb3f\ueb40\ueb41\ueb42\ueb43\ueb44\ueb45\ueb46\ueb47\ueb48\ueb49\ueb4a\ueb4b\ueb4c\ueb4d\ueb4e\ueb4f\ueb50\ueb51\ueb52\ueb53\ueb54\ueb55\ueb56\ueb57\ueb58\ueb59\ueb5a\ueb5b\ueb5c\ueb5d\ueb5e\ueb5f\ueb60\ueb61\ueb62\ueb63\ueb64\ueb65\ueb66\ueb67\ueb68\ueb69\ueb6a\ueb6b\ueb6c\ueb6d\ueb6e\ueb6f\ueb70\ueb71\ueb72\ueb73\ueb74\ueb75\ueb76\ueb77\ueb78\ueb79\ueb7a\ueb7b\ueb7c\ueb7d\ueb7e\ueb7f\ueb80\ueb81\ueb82\ueb83\ueb84\ueb85\ueb86\ueb87\ueb88\ueb89\ueb8a\ueb8b\ueb8c\ueb8d\ueb8e\ueb8f\ueb90\ueb91\ueb92\ueb93\ueb94\ueb95\ueb96\ueb97\ueb98\ueb99\ueb9a\ueb9b\ueb9c\ueb9d\ueb9e\ueb9f\ueba0\ueba1\ueba2\ueba3\ueba4\ueba5\ueba6\ueba7\ueba8\ueba9\uebaa\uebab\uebac\uebad\uebae\uebaf\uebb0\uebb1\uebb2\uebb3\uebb4\uebb5\uebb6\uebb7\uebb8\uebb9\uebba\uebbb\uebbc\uebbd\uebbe\uebbf\uebc0\uebc1\uebc2\uebc3\uebc4\uebc5\uebc6\uebc7\uebc8\uebc9\uebca\uebcb\uebcc\uebcd\uebce\uebcf\uebd0\uebd1\uebd2\uebd3\uebd4\uebd5\uebd6\uebd7\uebd8\uebd9\uebda\uebdb\uebdc\uebdd\uebde\uebdf\uebe0\uebe1\uebe2\uebe3\uebe4\uebe5\uebe6\uebe7\uebe8\uebe9\uebea\uebeb\uebec\uebed\uebee\uebef\uebf0\uebf1\uebf2\uebf3\uebf4\uebf5\uebf6\uebf7\uebf8\uebf9\uebfa\uebfb\uebfc\uebfd\uebfe\uebff\uec00\uec01\uec02\uec03\uec04\uec05\uec06\uec07\uec08\uec09\uec0a\uec0b\uec0c\uec0d\uec0e\uec0f\uec10\uec11\uec12\uec13\uec14\uec15\uec16\uec17\uec18\uec19\uec1a\uec1b\uec1c\uec1d\uec1e\uec1f\uec20\uec21\uec22\uec23\uec24\uec25\uec26\uec27\uec28\uec29\uec2a\uec2b\uec2c\uec2d\uec2e\uec2f\uec30\uec31\uec32\uec33\uec34\uec35\uec36\uec37\uec38\uec39\uec3a\uec3b\uec3c\uec3d\uec3e\uec3f\uec40\uec41\uec42\uec43\uec44\uec45\uec46\uec47\uec48\uec49\uec4a\uec4b\uec4c\uec4d\uec4e\uec4f\uec50\uec51\uec52\uec53\uec54\uec55\uec56\uec57\uec58\uec59\uec5a\uec5b\uec5c\uec5d\uec5e\uec5f\uec60\uec61\uec62\uec63\uec64\uec65\uec66\uec67\uec68\uec69\uec6a\uec6b\uec6c\uec6d\uec6e\uec6f\uec70\uec71\uec72\uec73\uec74\uec75\uec76\uec77\uec78\uec79\uec7a\uec7b\uec7c\uec7d\uec7e\uec7f\uec80\uec81\uec82\uec83\uec84\uec85\uec86\uec87\uec88\uec89\uec8a\uec8b\uec8c\uec8d\uec8e\uec8f\uec90\uec91\uec92\uec93\uec94\uec95\uec96\uec97\uec98\uec99\uec9a\uec9b\uec9c\uec9d\uec9e\uec9f\ueca0\ueca1\ueca2\ueca3\ueca4\ueca5\ueca6\ueca7\ueca8\ueca9\uecaa\uecab\uecac\uecad\uecae\uecaf\uecb0\uecb1\uecb2\uecb3\uecb4\uecb5\uecb6\uecb7\uecb8\uecb9\uecba\uecbb\uecbc\uecbd\uecbe\uecbf\uecc0\uecc1\uecc2\uecc3\uecc4\uecc5\uecc6\uecc7\uecc8\uecc9\uecca\ueccb\ueccc\ueccd\uecce\ueccf\uecd0\uecd1\uecd2\uecd3\uecd4\uecd5\uecd6\uecd7\uecd8\uecd9\uecda\uecdb\uecdc\uecdd\uecde\uecdf\uece0\uece1\uece2\uece3\uece4\uece5\uece6\uece7\uece8\uece9\uecea\ueceb\uecec\ueced\uecee\uecef\uecf0\uecf1\uecf2\uecf3\uecf4\uecf5\uecf6\uecf7\uecf8\uecf9\uecfa\uecfb\uecfc\uecfd\uecfe\uecff\ued00\ued01\ued02\ued03\ued04\ued05\ued06\ued07\ued08\ued09\ued0a\ued0b\ued0c\ued0d\ued0e\ued0f\ued10\ued11\ued12\ued13\ued14\ued15\ued16\ued17\ued18\ued19\ued1a\ued1b\ued1c\ued1d\ued1e\ued1f\ued20\ued21\ued22\ued23\ued24\ued25\ued26\ued27\ued28\ued29\ued2a\ued2b\ued2c\ued2d\ued2e\ued2f\ued30\ued31\ued32\ued33\ued34\ued35\ued36\ued37\ued38\ued39\ued3a\ued3b\ued3c\ued3d\ued3e\ued3f\ued40\ued41\ued42\ued43\ued44\ued45\ued46\ued47\ued48\ued49\ued4a\ued4b\ued4c\ued4d\ued4e\ued4f\ued50\ued51\ued52\ued53\ued54\ued55\ued56\ued57\ued58\ued59\ued5a\ued5b\ued5c\ued5d\ued5e\ued5f\ued60\ued61\ued62\ued63\ued64\ued65\ued66\ued67\ued68\ued69\ued6a\ued6b\ued6c\ued6d\ued6e\ued6f\ued70\ued71\ued72\ued73\ued74\ued75\ued76\ued77\ued78\ued79\ued7a\ued7b\ued7c\ued7d\ued7e\ued7f\ued80\ued81\ued82\ued83\ued84\ued85\ued86\ued87\ued88\ued89\ued8a\ued8b\ued8c\ued8d\ued8e\ued8f\ued90\ued91\ued92\ued93\ued94\ued95\ued96\ued97\ued98\ued99\ued9a\ued9b\ued9c\ued9d\ued9e\ued9f\ueda0\ueda1\ueda2\ueda3\ueda4\ueda5\ueda6\ueda7\ueda8\ueda9\uedaa\uedab\uedac\uedad\uedae\uedaf\uedb0\uedb1\uedb2\uedb3\uedb4\uedb5\uedb6\uedb7\uedb8\uedb9\uedba\uedbb\uedbc\uedbd\uedbe\uedbf\uedc0\uedc1\uedc2\uedc3\uedc4\uedc5\uedc6\uedc7\uedc8\uedc9\uedca\uedcb\uedcc\uedcd\uedce\uedcf\uedd0\uedd1\uedd2\uedd3\uedd4\uedd5\uedd6\uedd7\uedd8\uedd9\uedda\ueddb\ueddc\ueddd\uedde\ueddf\uede0\uede1\uede2\uede3\uede4\uede5\uede6\uede7\uede8\uede9\uedea\uedeb\uedec\ueded\uedee\uedef\uedf0\uedf1\uedf2\uedf3\uedf4\uedf5\uedf6\uedf7\uedf8\uedf9\uedfa\uedfb\uedfc\uedfd\uedfe\uedff\uee00\uee01\uee02\uee03\uee04\uee05\uee06\uee07\uee08\uee09\uee0a\uee0b\uee0c\uee0d\uee0e\uee0f\uee10\uee11\uee12\uee13\uee14\uee15\uee16\uee17\uee18\uee19\uee1a\uee1b\uee1c\uee1d\uee1e\uee1f\uee20\uee21\uee22\uee23\uee24\uee25\uee26\uee27\uee28\uee29\uee2a\uee2b\uee2c\uee2d\uee2e\uee2f\uee30\uee31\uee32\uee33\uee34\uee35\uee36\uee37\uee38\uee39\uee3a\uee3b\uee3c\uee3d\uee3e\uee3f\uee40\uee41\uee42\uee43\uee44\uee45\uee46\uee47\uee48\uee49\uee4a\uee4b\uee4c\uee4d\uee4e\uee4f\uee50\uee51\uee52\uee53\uee54\uee55\uee56\uee57\uee58\uee59\uee5a\uee5b\uee5c\uee5d\uee5e\uee5f\uee60\uee61\uee62\uee63\uee64\uee65\uee66\uee67\uee68\uee69\uee6a\uee6b\uee6c\uee6d\uee6e\uee6f\uee70\uee71\uee72\uee73\uee74\uee75\uee76\uee77\uee78\uee79\uee7a\uee7b\uee7c\uee7d\uee7e\uee7f\uee80\uee81\uee82\uee83\uee84\uee85\uee86\uee87\uee88\uee89\uee8a\uee8b\uee8c\uee8d\uee8e\uee8f\uee90\uee91\uee92\uee93\uee94\uee95\uee96\uee97\uee98\uee99\uee9a\uee9b\uee9c\uee9d\uee9e\uee9f\ueea0\ueea1\ueea2\ueea3\ueea4\ueea5\ueea6\ueea7\ueea8\ueea9\ueeaa\ueeab\ueeac\ueead\ueeae\ueeaf\ueeb0\ueeb1\ueeb2\ueeb3\ueeb4\ueeb5\ueeb6\ueeb7\ueeb8\ueeb9\ueeba\ueebb\ueebc\ueebd\ueebe\ueebf\ueec0\ueec1\ueec2\ueec3\ueec4\ueec5\ueec6\ueec7\ueec8\ueec9\ueeca\ueecb\ueecc\ueecd\ueece\ueecf\ueed0\ueed1\ueed2\ueed3\ueed4\ueed5\ueed6\ueed7\ueed8\ueed9\ueeda\ueedb\ueedc\ueedd\ueede\ueedf\ueee0\ueee1\ueee2\ueee3\ueee4\ueee5\ueee6\ueee7\ueee8\ueee9\ueeea\ueeeb\ueeec\ueeed\ueeee\ueeef\ueef0\ueef1\ueef2\ueef3\ueef4\ueef5\ueef6\ueef7\ueef8\ueef9\ueefa\ueefb\ueefc\ueefd\ueefe\ueeff\uef00\uef01\uef02\uef03\uef04\uef05\uef06\uef07\uef08\uef09\uef0a\uef0b\uef0c\uef0d\uef0e\uef0f\uef10\uef11\uef12\uef13\uef14\uef15\uef16\uef17\uef18\uef19\uef1a\uef1b\uef1c\uef1d\uef1e\uef1f\uef20\uef21\uef22\uef23\uef24\uef25\uef26\uef27\uef28\uef29\uef2a\uef2b\uef2c\uef2d\uef2e\uef2f\uef30\uef31\uef32\uef33\uef34\uef35\uef36\uef37\uef38\uef39\uef3a\uef3b\uef3c\uef3d\uef3e\uef3f\uef40\uef41\uef42\uef43\uef44\uef45\uef46\uef47\uef48\uef49\uef4a\uef4b\uef4c\uef4d\uef4e\uef4f\uef50\uef51\uef52\uef53\uef54\uef55\uef56\uef57\uef58\uef59\uef5a\uef5b\uef5c\uef5d\uef5e\uef5f\uef60\uef61\uef62\uef63\uef64\uef65\uef66\uef67\uef68\uef69\uef6a\uef6b\uef6c\uef6d\uef6e\uef6f\uef70\uef71\uef72\uef73\uef74\uef75\uef76\uef77\uef78\uef79\uef7a\uef7b\uef7c\uef7d\uef7e\uef7f\uef80\uef81\uef82\uef83\uef84\uef85\uef86\uef87\uef88\uef89\uef8a\uef8b\uef8c\uef8d\uef8e\uef8f\uef90\uef91\uef92\uef93\uef94\uef95\uef96\uef97\uef98\uef99\uef9a\uef9b\uef9c\uef9d\uef9e\uef9f\uefa0\uefa1\uefa2\uefa3\uefa4\uefa5\uefa6\uefa7\uefa8\uefa9\uefaa\uefab\uefac\uefad\uefae\uefaf\uefb0\uefb1\uefb2\uefb3\uefb4\uefb5\uefb6\uefb7\uefb8\uefb9\uefba\uefbb\uefbc\uefbd\uefbe\uefbf\uefc0\uefc1\uefc2\uefc3\uefc4\uefc5\uefc6\uefc7\uefc8\uefc9\uefca\uefcb\uefcc\uefcd\uefce\uefcf\uefd0\uefd1\uefd2\uefd3\uefd4\uefd5\uefd6\uefd7\uefd8\uefd9\uefda\uefdb\uefdc\uefdd\uefde\uefdf\uefe0\uefe1\uefe2\uefe3\uefe4\uefe5\uefe6\uefe7\uefe8\uefe9\uefea\uefeb\uefec\uefed\uefee\uefef\ueff0\ueff1\ueff2\ueff3\ueff4\ueff5\ueff6\ueff7\ueff8\ueff9\ueffa\ueffb\ueffc\ueffd\ueffe\uefff\uf000\uf001\uf002\uf003\uf004\uf005\uf006\uf007\uf008\uf009\uf00a\uf00b\uf00c\uf00d\uf00e\uf00f\uf010\uf011\uf012\uf013\uf014\uf015\uf016\uf017\uf018\uf019\uf01a\uf01b\uf01c\uf01d\uf01e\uf01f\uf020\uf021\uf022\uf023\uf024\uf025\uf026\uf027\uf028\uf029\uf02a\uf02b\uf02c\uf02d\uf02e\uf02f\uf030\uf031\uf032\uf033\uf034\uf035\uf036\uf037\uf038\uf039\uf03a\uf03b\uf03c\uf03d\uf03e\uf03f\uf040\uf041\uf042\uf043\uf044\uf045\uf046\uf047\uf048\uf049\uf04a\uf04b\uf04c\uf04d\uf04e\uf04f\uf050\uf051\uf052\uf053\uf054\uf055\uf056\uf057\uf058\uf059\uf05a\uf05b\uf05c\uf05d\uf05e\uf05f\uf060\uf061\uf062\uf063\uf064\uf065\uf066\uf067\uf068\uf069\uf06a\uf06b\uf06c\uf06d\uf06e\uf06f\uf070\uf071\uf072\uf073\uf074\uf075\uf076\uf077\uf078\uf079\uf07a\uf07b\uf07c\uf07d\uf07e\uf07f\uf080\uf081\uf082\uf083\uf084\uf085\uf086\uf087\uf088\uf089\uf08a\uf08b\uf08c\uf08d\uf08e\uf08f\uf090\uf091\uf092\uf093\uf094\uf095\uf096\uf097\uf098\uf099\uf09a\uf09b\uf09c\uf09d\uf09e\uf09f\uf0a0\uf0a1\uf0a2\uf0a3\uf0a4\uf0a5\uf0a6\uf0a7\uf0a8\uf0a9\uf0aa\uf0ab\uf0ac\uf0ad\uf0ae\uf0af\uf0b0\uf0b1\uf0b2\uf0b3\uf0b4\uf0b5\uf0b6\uf0b7\uf0b8\uf0b9\uf0ba\uf0bb\uf0bc\uf0bd\uf0be\uf0bf\uf0c0\uf0c1\uf0c2\uf0c3\uf0c4\uf0c5\uf0c6\uf0c7\uf0c8\uf0c9\uf0ca\uf0cb\uf0cc\uf0cd\uf0ce\uf0cf\uf0d0\uf0d1\uf0d2\uf0d3\uf0d4\uf0d5\uf0d6\uf0d7\uf0d8\uf0d9\uf0da\uf0db\uf0dc\uf0dd\uf0de\uf0df\uf0e0\uf0e1\uf0e2\uf0e3\uf0e4\uf0e5\uf0e6\uf0e7\uf0e8\uf0e9\uf0ea\uf0eb\uf0ec\uf0ed\uf0ee\uf0ef\uf0f0\uf0f1\uf0f2\uf0f3\uf0f4\uf0f5\uf0f6\uf0f7\uf0f8\uf0f9\uf0fa\uf0fb\uf0fc\uf0fd\uf0fe\uf0ff\uf100\uf101\uf102\uf103\uf104\uf105\uf106\uf107\uf108\uf109\uf10a\uf10b\uf10c\uf10d\uf10e\uf10f\uf110\uf111\uf112\uf113\uf114\uf115\uf116\uf117\uf118\uf119\uf11a\uf11b\uf11c\uf11d\uf11e\uf11f\uf120\uf121\uf122\uf123\uf124\uf125\uf126\uf127\uf128\uf129\uf12a\uf12b\uf12c\uf12d\uf12e\uf12f\uf130\uf131\uf132\uf133\uf134\uf135\uf136\uf137\uf138\uf139\uf13a\uf13b\uf13c\uf13d\uf13e\uf13f\uf140\uf141\uf142\uf143\uf144\uf145\uf146\uf147\uf148\uf149\uf14a\uf14b\uf14c\uf14d\uf14e\uf14f\uf150\uf151\uf152\uf153\uf154\uf155\uf156\uf157\uf158\uf159\uf15a\uf15b\uf15c\uf15d\uf15e\uf15f\uf160\uf161\uf162\uf163\uf164\uf165\uf166\uf167\uf168\uf169\uf16a\uf16b\uf16c\uf16d\uf16e\uf16f\uf170\uf171\uf172\uf173\uf174\uf175\uf176\uf177\uf178\uf179\uf17a\uf17b\uf17c\uf17d\uf17e\uf17f\uf180\uf181\uf182\uf183\uf184\uf185\uf186\uf187\uf188\uf189\uf18a\uf18b\uf18c\uf18d\uf18e\uf18f\uf190\uf191\uf192\uf193\uf194\uf195\uf196\uf197\uf198\uf199\uf19a\uf19b\uf19c\uf19d\uf19e\uf19f\uf1a0\uf1a1\uf1a2\uf1a3\uf1a4\uf1a5\uf1a6\uf1a7\uf1a8\uf1a9\uf1aa\uf1ab\uf1ac\uf1ad\uf1ae\uf1af\uf1b0\uf1b1\uf1b2\uf1b3\uf1b4\uf1b5\uf1b6\uf1b7\uf1b8\uf1b9\uf1ba\uf1bb\uf1bc\uf1bd\uf1be\uf1bf\uf1c0\uf1c1\uf1c2\uf1c3\uf1c4\uf1c5\uf1c6\uf1c7\uf1c8\uf1c9\uf1ca\uf1cb\uf1cc\uf1cd\uf1ce\uf1cf\uf1d0\uf1d1\uf1d2\uf1d3\uf1d4\uf1d5\uf1d6\uf1d7\uf1d8\uf1d9\uf1da\uf1db\uf1dc\uf1dd\uf1de\uf1df\uf1e0\uf1e1\uf1e2\uf1e3\uf1e4\uf1e5\uf1e6\uf1e7\uf1e8\uf1e9\uf1ea\uf1eb\uf1ec\uf1ed\uf1ee\uf1ef\uf1f0\uf1f1\uf1f2\uf1f3\uf1f4\uf1f5\uf1f6\uf1f7\uf1f8\uf1f9\uf1fa\uf1fb\uf1fc\uf1fd\uf1fe\uf1ff\uf200\uf201\uf202\uf203\uf204\uf205\uf206\uf207\uf208\uf209\uf20a\uf20b\uf20c\uf20d\uf20e\uf20f\uf210\uf211\uf212\uf213\uf214\uf215\uf216\uf217\uf218\uf219\uf21a\uf21b\uf21c\uf21d\uf21e\uf21f\uf220\uf221\uf222\uf223\uf224\uf225\uf226\uf227\uf228\uf229\uf22a\uf22b\uf22c\uf22d\uf22e\uf22f\uf230\uf231\uf232\uf233\uf234\uf235\uf236\uf237\uf238\uf239\uf23a\uf23b\uf23c\uf23d\uf23e\uf23f\uf240\uf241\uf242\uf243\uf244\uf245\uf246\uf247\uf248\uf249\uf24a\uf24b\uf24c\uf24d\uf24e\uf24f\uf250\uf251\uf252\uf253\uf254\uf255\uf256\uf257\uf258\uf259\uf25a\uf25b\uf25c\uf25d\uf25e\uf25f\uf260\uf261\uf262\uf263\uf264\uf265\uf266\uf267\uf268\uf269\uf26a\uf26b\uf26c\uf26d\uf26e\uf26f\uf270\uf271\uf272\uf273\uf274\uf275\uf276\uf277\uf278\uf279\uf27a\uf27b\uf27c\uf27d\uf27e\uf27f\uf280\uf281\uf282\uf283\uf284\uf285\uf286\uf287\uf288\uf289\uf28a\uf28b\uf28c\uf28d\uf28e\uf28f\uf290\uf291\uf292\uf293\uf294\uf295\uf296\uf297\uf298\uf299\uf29a\uf29b\uf29c\uf29d\uf29e\uf29f\uf2a0\uf2a1\uf2a2\uf2a3\uf2a4\uf2a5\uf2a6\uf2a7\uf2a8\uf2a9\uf2aa\uf2ab\uf2ac\uf2ad\uf2ae\uf2af\uf2b0\uf2b1\uf2b2\uf2b3\uf2b4\uf2b5\uf2b6\uf2b7\uf2b8\uf2b9\uf2ba\uf2bb\uf2bc\uf2bd\uf2be\uf2bf\uf2c0\uf2c1\uf2c2\uf2c3\uf2c4\uf2c5\uf2c6\uf2c7\uf2c8\uf2c9\uf2ca\uf2cb\uf2cc\uf2cd\uf2ce\uf2cf\uf2d0\uf2d1\uf2d2\uf2d3\uf2d4\uf2d5\uf2d6\uf2d7\uf2d8\uf2d9\uf2da\uf2db\uf2dc\uf2dd\uf2de\uf2df\uf2e0\uf2e1\uf2e2\uf2e3\uf2e4\uf2e5\uf2e6\uf2e7\uf2e8\uf2e9\uf2ea\uf2eb\uf2ec\uf2ed\uf2ee\uf2ef\uf2f0\uf2f1\uf2f2\uf2f3\uf2f4\uf2f5\uf2f6\uf2f7\uf2f8\uf2f9\uf2fa\uf2fb\uf2fc\uf2fd\uf2fe\uf2ff\uf300\uf301\uf302\uf303\uf304\uf305\uf306\uf307\uf308\uf309\uf30a\uf30b\uf30c\uf30d\uf30e\uf30f\uf310\uf311\uf312\uf313\uf314\uf315\uf316\uf317\uf318\uf319\uf31a\uf31b\uf31c\uf31d\uf31e\uf31f\uf320\uf321\uf322\uf323\uf324\uf325\uf326\uf327\uf328\uf329\uf32a\uf32b\uf32c\uf32d\uf32e\uf32f\uf330\uf331\uf332\uf333\uf334\uf335\uf336\uf337\uf338\uf339\uf33a\uf33b\uf33c\uf33d\uf33e\uf33f\uf340\uf341\uf342\uf343\uf344\uf345\uf346\uf347\uf348\uf349\uf34a\uf34b\uf34c\uf34d\uf34e\uf34f\uf350\uf351\uf352\uf353\uf354\uf355\uf356\uf357\uf358\uf359\uf35a\uf35b\uf35c\uf35d\uf35e\uf35f\uf360\uf361\uf362\uf363\uf364\uf365\uf366\uf367\uf368\uf369\uf36a\uf36b\uf36c\uf36d\uf36e\uf36f\uf370\uf371\uf372\uf373\uf374\uf375\uf376\uf377\uf378\uf379\uf37a\uf37b\uf37c\uf37d\uf37e\uf37f\uf380\uf381\uf382\uf383\uf384\uf385\uf386\uf387\uf388\uf389\uf38a\uf38b\uf38c\uf38d\uf38e\uf38f\uf390\uf391\uf392\uf393\uf394\uf395\uf396\uf397\uf398\uf399\uf39a\uf39b\uf39c\uf39d\uf39e\uf39f\uf3a0\uf3a1\uf3a2\uf3a3\uf3a4\uf3a5\uf3a6\uf3a7\uf3a8\uf3a9\uf3aa\uf3ab\uf3ac\uf3ad\uf3ae\uf3af\uf3b0\uf3b1\uf3b2\uf3b3\uf3b4\uf3b5\uf3b6\uf3b7\uf3b8\uf3b9\uf3ba\uf3bb\uf3bc\uf3bd\uf3be\uf3bf\uf3c0\uf3c1\uf3c2\uf3c3\uf3c4\uf3c5\uf3c6\uf3c7\uf3c8\uf3c9\uf3ca\uf3cb\uf3cc\uf3cd\uf3ce\uf3cf\uf3d0\uf3d1\uf3d2\uf3d3\uf3d4\uf3d5\uf3d6\uf3d7\uf3d8\uf3d9\uf3da\uf3db\uf3dc\uf3dd\uf3de\uf3df\uf3e0\uf3e1\uf3e2\uf3e3\uf3e4\uf3e5\uf3e6\uf3e7\uf3e8\uf3e9\uf3ea\uf3eb\uf3ec\uf3ed\uf3ee\uf3ef\uf3f0\uf3f1\uf3f2\uf3f3\uf3f4\uf3f5\uf3f6\uf3f7\uf3f8\uf3f9\uf3fa\uf3fb\uf3fc\uf3fd\uf3fe\uf3ff\uf400\uf401\uf402\uf403\uf404\uf405\uf406\uf407\uf408\uf409\uf40a\uf40b\uf40c\uf40d\uf40e\uf40f\uf410\uf411\uf412\uf413\uf414\uf415\uf416\uf417\uf418\uf419\uf41a\uf41b\uf41c\uf41d\uf41e\uf41f\uf420\uf421\uf422\uf423\uf424\uf425\uf426\uf427\uf428\uf429\uf42a\uf42b\uf42c\uf42d\uf42e\uf42f\uf430\uf431\uf432\uf433\uf434\uf435\uf436\uf437\uf438\uf439\uf43a\uf43b\uf43c\uf43d\uf43e\uf43f\uf440\uf441\uf442\uf443\uf444\uf445\uf446\uf447\uf448\uf449\uf44a\uf44b\uf44c\uf44d\uf44e\uf44f\uf450\uf451\uf452\uf453\uf454\uf455\uf456\uf457\uf458\uf459\uf45a\uf45b\uf45c\uf45d\uf45e\uf45f\uf460\uf461\uf462\uf463\uf464\uf465\uf466\uf467\uf468\uf469\uf46a\uf46b\uf46c\uf46d\uf46e\uf46f\uf470\uf471\uf472\uf473\uf474\uf475\uf476\uf477\uf478\uf479\uf47a\uf47b\uf47c\uf47d\uf47e\uf47f\uf480\uf481\uf482\uf483\uf484\uf485\uf486\uf487\uf488\uf489\uf48a\uf48b\uf48c\uf48d\uf48e\uf48f\uf490\uf491\uf492\uf493\uf494\uf495\uf496\uf497\uf498\uf499\uf49a\uf49b\uf49c\uf49d\uf49e\uf49f\uf4a0\uf4a1\uf4a2\uf4a3\uf4a4\uf4a5\uf4a6\uf4a7\uf4a8\uf4a9\uf4aa\uf4ab\uf4ac\uf4ad\uf4ae\uf4af\uf4b0\uf4b1\uf4b2\uf4b3\uf4b4\uf4b5\uf4b6\uf4b7\uf4b8\uf4b9\uf4ba\uf4bb\uf4bc\uf4bd\uf4be\uf4bf\uf4c0\uf4c1\uf4c2\uf4c3\uf4c4\uf4c5\uf4c6\uf4c7\uf4c8\uf4c9\uf4ca\uf4cb\uf4cc\uf4cd\uf4ce\uf4cf\uf4d0\uf4d1\uf4d2\uf4d3\uf4d4\uf4d5\uf4d6\uf4d7\uf4d8\uf4d9\uf4da\uf4db\uf4dc\uf4dd\uf4de\uf4df\uf4e0\uf4e1\uf4e2\uf4e3\uf4e4\uf4e5\uf4e6\uf4e7\uf4e8\uf4e9\uf4ea\uf4eb\uf4ec\uf4ed\uf4ee\uf4ef\uf4f0\uf4f1\uf4f2\uf4f3\uf4f4\uf4f5\uf4f6\uf4f7\uf4f8\uf4f9\uf4fa\uf4fb\uf4fc\uf4fd\uf4fe\uf4ff\uf500\uf501\uf502\uf503\uf504\uf505\uf506\uf507\uf508\uf509\uf50a\uf50b\uf50c\uf50d\uf50e\uf50f\uf510\uf511\uf512\uf513\uf514\uf515\uf516\uf517\uf518\uf519\uf51a\uf51b\uf51c\uf51d\uf51e\uf51f\uf520\uf521\uf522\uf523\uf524\uf525\uf526\uf527\uf528\uf529\uf52a\uf52b\uf52c\uf52d\uf52e\uf52f\uf530\uf531\uf532\uf533\uf534\uf535\uf536\uf537\uf538\uf539\uf53a\uf53b\uf53c\uf53d\uf53e\uf53f\uf540\uf541\uf542\uf543\uf544\uf545\uf546\uf547\uf548\uf549\uf54a\uf54b\uf54c\uf54d\uf54e\uf54f\uf550\uf551\uf552\uf553\uf554\uf555\uf556\uf557\uf558\uf559\uf55a\uf55b\uf55c\uf55d\uf55e\uf55f\uf560\uf561\uf562\uf563\uf564\uf565\uf566\uf567\uf568\uf569\uf56a\uf56b\uf56c\uf56d\uf56e\uf56f\uf570\uf571\uf572\uf573\uf574\uf575\uf576\uf577\uf578\uf579\uf57a\uf57b\uf57c\uf57d\uf57e\uf57f\uf580\uf581\uf582\uf583\uf584\uf585\uf586\uf587\uf588\uf589\uf58a\uf58b\uf58c\uf58d\uf58e\uf58f\uf590\uf591\uf592\uf593\uf594\uf595\uf596\uf597\uf598\uf599\uf59a\uf59b\uf59c\uf59d\uf59e\uf59f\uf5a0\uf5a1\uf5a2\uf5a3\uf5a4\uf5a5\uf5a6\uf5a7\uf5a8\uf5a9\uf5aa\uf5ab\uf5ac\uf5ad\uf5ae\uf5af\uf5b0\uf5b1\uf5b2\uf5b3\uf5b4\uf5b5\uf5b6\uf5b7\uf5b8\uf5b9\uf5ba\uf5bb\uf5bc\uf5bd\uf5be\uf5bf\uf5c0\uf5c1\uf5c2\uf5c3\uf5c4\uf5c5\uf5c6\uf5c7\uf5c8\uf5c9\uf5ca\uf5cb\uf5cc\uf5cd\uf5ce\uf5cf\uf5d0\uf5d1\uf5d2\uf5d3\uf5d4\uf5d5\uf5d6\uf5d7\uf5d8\uf5d9\uf5da\uf5db\uf5dc\uf5dd\uf5de\uf5df\uf5e0\uf5e1\uf5e2\uf5e3\uf5e4\uf5e5\uf5e6\uf5e7\uf5e8\uf5e9\uf5ea\uf5eb\uf5ec\uf5ed\uf5ee\uf5ef\uf5f0\uf5f1\uf5f2\uf5f3\uf5f4\uf5f5\uf5f6\uf5f7\uf5f8\uf5f9\uf5fa\uf5fb\uf5fc\uf5fd\uf5fe\uf5ff\uf600\uf601\uf602\uf603\uf604\uf605\uf606\uf607\uf608\uf609\uf60a\uf60b\uf60c\uf60d\uf60e\uf60f\uf610\uf611\uf612\uf613\uf614\uf615\uf616\uf617\uf618\uf619\uf61a\uf61b\uf61c\uf61d\uf61e\uf61f\uf620\uf621\uf622\uf623\uf624\uf625\uf626\uf627\uf628\uf629\uf62a\uf62b\uf62c\uf62d\uf62e\uf62f\uf630\uf631\uf632\uf633\uf634\uf635\uf636\uf637\uf638\uf639\uf63a\uf63b\uf63c\uf63d\uf63e\uf63f\uf640\uf641\uf642\uf643\uf644\uf645\uf646\uf647\uf648\uf649\uf64a\uf64b\uf64c\uf64d\uf64e\uf64f\uf650\uf651\uf652\uf653\uf654\uf655\uf656\uf657\uf658\uf659\uf65a\uf65b\uf65c\uf65d\uf65e\uf65f\uf660\uf661\uf662\uf663\uf664\uf665\uf666\uf667\uf668\uf669\uf66a\uf66b\uf66c\uf66d\uf66e\uf66f\uf670\uf671\uf672\uf673\uf674\uf675\uf676\uf677\uf678\uf679\uf67a\uf67b\uf67c\uf67d\uf67e\uf67f\uf680\uf681\uf682\uf683\uf684\uf685\uf686\uf687\uf688\uf689\uf68a\uf68b\uf68c\uf68d\uf68e\uf68f\uf690\uf691\uf692\uf693\uf694\uf695\uf696\uf697\uf698\uf699\uf69a\uf69b\uf69c\uf69d\uf69e\uf69f\uf6a0\uf6a1\uf6a2\uf6a3\uf6a4\uf6a5\uf6a6\uf6a7\uf6a8\uf6a9\uf6aa\uf6ab\uf6ac\uf6ad\uf6ae\uf6af\uf6b0\uf6b1\uf6b2\uf6b3\uf6b4\uf6b5\uf6b6\uf6b7\uf6b8\uf6b9\uf6ba\uf6bb\uf6bc\uf6bd\uf6be\uf6bf\uf6c0\uf6c1\uf6c2\uf6c3\uf6c4\uf6c5\uf6c6\uf6c7\uf6c8\uf6c9\uf6ca\uf6cb\uf6cc\uf6cd\uf6ce\uf6cf\uf6d0\uf6d1\uf6d2\uf6d3\uf6d4\uf6d5\uf6d6\uf6d7\uf6d8\uf6d9\uf6da\uf6db\uf6dc\uf6dd\uf6de\uf6df\uf6e0\uf6e1\uf6e2\uf6e3\uf6e4\uf6e5\uf6e6\uf6e7\uf6e8\uf6e9\uf6ea\uf6eb\uf6ec\uf6ed\uf6ee\uf6ef\uf6f0\uf6f1\uf6f2\uf6f3\uf6f4\uf6f5\uf6f6\uf6f7\uf6f8\uf6f9\uf6fa\uf6fb\uf6fc\uf6fd\uf6fe\uf6ff\uf700\uf701\uf702\uf703\uf704\uf705\uf706\uf707\uf708\uf709\uf70a\uf70b\uf70c\uf70d\uf70e\uf70f\uf710\uf711\uf712\uf713\uf714\uf715\uf716\uf717\uf718\uf719\uf71a\uf71b\uf71c\uf71d\uf71e\uf71f\uf720\uf721\uf722\uf723\uf724\uf725\uf726\uf727\uf728\uf729\uf72a\uf72b\uf72c\uf72d\uf72e\uf72f\uf730\uf731\uf732\uf733\uf734\uf735\uf736\uf737\uf738\uf739\uf73a\uf73b\uf73c\uf73d\uf73e\uf73f\uf740\uf741\uf742\uf743\uf744\uf745\uf746\uf747\uf748\uf749\uf74a\uf74b\uf74c\uf74d\uf74e\uf74f\uf750\uf751\uf752\uf753\uf754\uf755\uf756\uf757\uf758\uf759\uf75a\uf75b\uf75c\uf75d\uf75e\uf75f\uf760\uf761\uf762\uf763\uf764\uf765\uf766\uf767\uf768\uf769\uf76a\uf76b\uf76c\uf76d\uf76e\uf76f\uf770\uf771\uf772\uf773\uf774\uf775\uf776\uf777\uf778\uf779\uf77a\uf77b\uf77c\uf77d\uf77e\uf77f\uf780\uf781\uf782\uf783\uf784\uf785\uf786\uf787\uf788\uf789\uf78a\uf78b\uf78c\uf78d\uf78e\uf78f\uf790\uf791\uf792\uf793\uf794\uf795\uf796\uf797\uf798\uf799\uf79a\uf79b\uf79c\uf79d\uf79e\uf79f\uf7a0\uf7a1\uf7a2\uf7a3\uf7a4\uf7a5\uf7a6\uf7a7\uf7a8\uf7a9\uf7aa\uf7ab\uf7ac\uf7ad\uf7ae\uf7af\uf7b0\uf7b1\uf7b2\uf7b3\uf7b4\uf7b5\uf7b6\uf7b7\uf7b8\uf7b9\uf7ba\uf7bb\uf7bc\uf7bd\uf7be\uf7bf\uf7c0\uf7c1\uf7c2\uf7c3\uf7c4\uf7c5\uf7c6\uf7c7\uf7c8\uf7c9\uf7ca\uf7cb\uf7cc\uf7cd\uf7ce\uf7cf\uf7d0\uf7d1\uf7d2\uf7d3\uf7d4\uf7d5\uf7d6\uf7d7\uf7d8\uf7d9\uf7da\uf7db\uf7dc\uf7dd\uf7de\uf7df\uf7e0\uf7e1\uf7e2\uf7e3\uf7e4\uf7e5\uf7e6\uf7e7\uf7e8\uf7e9\uf7ea\uf7eb\uf7ec\uf7ed\uf7ee\uf7ef\uf7f0\uf7f1\uf7f2\uf7f3\uf7f4\uf7f5\uf7f6\uf7f7\uf7f8\uf7f9\uf7fa\uf7fb\uf7fc\uf7fd\uf7fe\uf7ff\uf800\uf801\uf802\uf803\uf804\uf805\uf806\uf807\uf808\uf809\uf80a\uf80b\uf80c\uf80d\uf80e\uf80f\uf810\uf811\uf812\uf813\uf814\uf815\uf816\uf817\uf818\uf819\uf81a\uf81b\uf81c\uf81d\uf81e\uf81f\uf820\uf821\uf822\uf823\uf824\uf825\uf826\uf827\uf828\uf829\uf82a\uf82b\uf82c\uf82d\uf82e\uf82f\uf830\uf831\uf832\uf833\uf834\uf835\uf836\uf837\uf838\uf839\uf83a\uf83b\uf83c\uf83d\uf83e\uf83f\uf840\uf841\uf842\uf843\uf844\uf845\uf846\uf847\uf848\uf849\uf84a\uf84b\uf84c\uf84d\uf84e\uf84f\uf850\uf851\uf852\uf853\uf854\uf855\uf856\uf857\uf858\uf859\uf85a\uf85b\uf85c\uf85d\uf85e\uf85f\uf860\uf861\uf862\uf863\uf864\uf865\uf866\uf867\uf868\uf869\uf86a\uf86b\uf86c\uf86d\uf86e\uf86f\uf870\uf871\uf872\uf873\uf874\uf875\uf876\uf877\uf878\uf879\uf87a\uf87b\uf87c\uf87d\uf87e\uf87f\uf880\uf881\uf882\uf883\uf884\uf885\uf886\uf887\uf888\uf889\uf88a\uf88b\uf88c\uf88d\uf88e\uf88f\uf890\uf891\uf892\uf893\uf894\uf895\uf896\uf897\uf898\uf899\uf89a\uf89b\uf89c\uf89d\uf89e\uf89f\uf8a0\uf8a1\uf8a2\uf8a3\uf8a4\uf8a5\uf8a6\uf8a7\uf8a8\uf8a9\uf8aa\uf8ab\uf8ac\uf8ad\uf8ae\uf8af\uf8b0\uf8b1\uf8b2\uf8b3\uf8b4\uf8b5\uf8b6\uf8b7\uf8b8\uf8b9\uf8ba\uf8bb\uf8bc\uf8bd\uf8be\uf8bf\uf8c0\uf8c1\uf8c2\uf8c3\uf8c4\uf8c5\uf8c6\uf8c7\uf8c8\uf8c9\uf8ca\uf8cb\uf8cc\uf8cd\uf8ce\uf8cf\uf8d0\uf8d1\uf8d2\uf8d3\uf8d4\uf8d5\uf8d6\uf8d7\uf8d8\uf8d9\uf8da\uf8db\uf8dc\uf8dd\uf8de\uf8df\uf8e0\uf8e1\uf8e2\uf8e3\uf8e4\uf8e5\uf8e6\uf8e7\uf8e8\uf8e9\uf8ea\uf8eb\uf8ec\uf8ed\uf8ee\uf8ef\uf8f0\uf8f1\uf8f2\uf8f3\uf8f4\uf8f5\uf8f6\uf8f7\uf8f8\uf8f9\uf8fa\uf8fb\uf8fc\uf8fd\uf8fe\uf8ff' + +try: + Cs = eval(r"'\ud800\ud801\ud802\ud803\ud804\ud805\ud806\ud807\ud808\ud809\ud80a\ud80b\ud80c\ud80d\ud80e\ud80f\ud810\ud811\ud812\ud813\ud814\ud815\ud816\ud817\ud818\ud819\ud81a\ud81b\ud81c\ud81d\ud81e\ud81f\ud820\ud821\ud822\ud823\ud824\ud825\ud826\ud827\ud828\ud829\ud82a\ud82b\ud82c\ud82d\ud82e\ud82f\ud830\ud831\ud832\ud833\ud834\ud835\ud836\ud837\ud838\ud839\ud83a\ud83b\ud83c\ud83d\ud83e\ud83f\ud840\ud841\ud842\ud843\ud844\ud845\ud846\ud847\ud848\ud849\ud84a\ud84b\ud84c\ud84d\ud84e\ud84f\ud850\ud851\ud852\ud853\ud854\ud855\ud856\ud857\ud858\ud859\ud85a\ud85b\ud85c\ud85d\ud85e\ud85f\ud860\ud861\ud862\ud863\ud864\ud865\ud866\ud867\ud868\ud869\ud86a\ud86b\ud86c\ud86d\ud86e\ud86f\ud870\ud871\ud872\ud873\ud874\ud875\ud876\ud877\ud878\ud879\ud87a\ud87b\ud87c\ud87d\ud87e\ud87f\ud880\ud881\ud882\ud883\ud884\ud885\ud886\ud887\ud888\ud889\ud88a\ud88b\ud88c\ud88d\ud88e\ud88f\ud890\ud891\ud892\ud893\ud894\ud895\ud896\ud897\ud898\ud899\ud89a\ud89b\ud89c\ud89d\ud89e\ud89f\ud8a0\ud8a1\ud8a2\ud8a3\ud8a4\ud8a5\ud8a6\ud8a7\ud8a8\ud8a9\ud8aa\ud8ab\ud8ac\ud8ad\ud8ae\ud8af\ud8b0\ud8b1\ud8b2\ud8b3\ud8b4\ud8b5\ud8b6\ud8b7\ud8b8\ud8b9\ud8ba\ud8bb\ud8bc\ud8bd\ud8be\ud8bf\ud8c0\ud8c1\ud8c2\ud8c3\ud8c4\ud8c5\ud8c6\ud8c7\ud8c8\ud8c9\ud8ca\ud8cb\ud8cc\ud8cd\ud8ce\ud8cf\ud8d0\ud8d1\ud8d2\ud8d3\ud8d4\ud8d5\ud8d6\ud8d7\ud8d8\ud8d9\ud8da\ud8db\ud8dc\ud8dd\ud8de\ud8df\ud8e0\ud8e1\ud8e2\ud8e3\ud8e4\ud8e5\ud8e6\ud8e7\ud8e8\ud8e9\ud8ea\ud8eb\ud8ec\ud8ed\ud8ee\ud8ef\ud8f0\ud8f1\ud8f2\ud8f3\ud8f4\ud8f5\ud8f6\ud8f7\ud8f8\ud8f9\ud8fa\ud8fb\ud8fc\ud8fd\ud8fe\ud8ff\ud900\ud901\ud902\ud903\ud904\ud905\ud906\ud907\ud908\ud909\ud90a\ud90b\ud90c\ud90d\ud90e\ud90f\ud910\ud911\ud912\ud913\ud914\ud915\ud916\ud917\ud918\ud919\ud91a\ud91b\ud91c\ud91d\ud91e\ud91f\ud920\ud921\ud922\ud923\ud924\ud925\ud926\ud927\ud928\ud929\ud92a\ud92b\ud92c\ud92d\ud92e\ud92f\ud930\ud931\ud932\ud933\ud934\ud935\ud936\ud937\ud938\ud939\ud93a\ud93b\ud93c\ud93d\ud93e\ud93f\ud940\ud941\ud942\ud943\ud944\ud945\ud946\ud947\ud948\ud949\ud94a\ud94b\ud94c\ud94d\ud94e\ud94f\ud950\ud951\ud952\ud953\ud954\ud955\ud956\ud957\ud958\ud959\ud95a\ud95b\ud95c\ud95d\ud95e\ud95f\ud960\ud961\ud962\ud963\ud964\ud965\ud966\ud967\ud968\ud969\ud96a\ud96b\ud96c\ud96d\ud96e\ud96f\ud970\ud971\ud972\ud973\ud974\ud975\ud976\ud977\ud978\ud979\ud97a\ud97b\ud97c\ud97d\ud97e\ud97f\ud980\ud981\ud982\ud983\ud984\ud985\ud986\ud987\ud988\ud989\ud98a\ud98b\ud98c\ud98d\ud98e\ud98f\ud990\ud991\ud992\ud993\ud994\ud995\ud996\ud997\ud998\ud999\ud99a\ud99b\ud99c\ud99d\ud99e\ud99f\ud9a0\ud9a1\ud9a2\ud9a3\ud9a4\ud9a5\ud9a6\ud9a7\ud9a8\ud9a9\ud9aa\ud9ab\ud9ac\ud9ad\ud9ae\ud9af\ud9b0\ud9b1\ud9b2\ud9b3\ud9b4\ud9b5\ud9b6\ud9b7\ud9b8\ud9b9\ud9ba\ud9bb\ud9bc\ud9bd\ud9be\ud9bf\ud9c0\ud9c1\ud9c2\ud9c3\ud9c4\ud9c5\ud9c6\ud9c7\ud9c8\ud9c9\ud9ca\ud9cb\ud9cc\ud9cd\ud9ce\ud9cf\ud9d0\ud9d1\ud9d2\ud9d3\ud9d4\ud9d5\ud9d6\ud9d7\ud9d8\ud9d9\ud9da\ud9db\ud9dc\ud9dd\ud9de\ud9df\ud9e0\ud9e1\ud9e2\ud9e3\ud9e4\ud9e5\ud9e6\ud9e7\ud9e8\ud9e9\ud9ea\ud9eb\ud9ec\ud9ed\ud9ee\ud9ef\ud9f0\ud9f1\ud9f2\ud9f3\ud9f4\ud9f5\ud9f6\ud9f7\ud9f8\ud9f9\ud9fa\ud9fb\ud9fc\ud9fd\ud9fe\ud9ff\uda00\uda01\uda02\uda03\uda04\uda05\uda06\uda07\uda08\uda09\uda0a\uda0b\uda0c\uda0d\uda0e\uda0f\uda10\uda11\uda12\uda13\uda14\uda15\uda16\uda17\uda18\uda19\uda1a\uda1b\uda1c\uda1d\uda1e\uda1f\uda20\uda21\uda22\uda23\uda24\uda25\uda26\uda27\uda28\uda29\uda2a\uda2b\uda2c\uda2d\uda2e\uda2f\uda30\uda31\uda32\uda33\uda34\uda35\uda36\uda37\uda38\uda39\uda3a\uda3b\uda3c\uda3d\uda3e\uda3f\uda40\uda41\uda42\uda43\uda44\uda45\uda46\uda47\uda48\uda49\uda4a\uda4b\uda4c\uda4d\uda4e\uda4f\uda50\uda51\uda52\uda53\uda54\uda55\uda56\uda57\uda58\uda59\uda5a\uda5b\uda5c\uda5d\uda5e\uda5f\uda60\uda61\uda62\uda63\uda64\uda65\uda66\uda67\uda68\uda69\uda6a\uda6b\uda6c\uda6d\uda6e\uda6f\uda70\uda71\uda72\uda73\uda74\uda75\uda76\uda77\uda78\uda79\uda7a\uda7b\uda7c\uda7d\uda7e\uda7f\uda80\uda81\uda82\uda83\uda84\uda85\uda86\uda87\uda88\uda89\uda8a\uda8b\uda8c\uda8d\uda8e\uda8f\uda90\uda91\uda92\uda93\uda94\uda95\uda96\uda97\uda98\uda99\uda9a\uda9b\uda9c\uda9d\uda9e\uda9f\udaa0\udaa1\udaa2\udaa3\udaa4\udaa5\udaa6\udaa7\udaa8\udaa9\udaaa\udaab\udaac\udaad\udaae\udaaf\udab0\udab1\udab2\udab3\udab4\udab5\udab6\udab7\udab8\udab9\udaba\udabb\udabc\udabd\udabe\udabf\udac0\udac1\udac2\udac3\udac4\udac5\udac6\udac7\udac8\udac9\udaca\udacb\udacc\udacd\udace\udacf\udad0\udad1\udad2\udad3\udad4\udad5\udad6\udad7\udad8\udad9\udada\udadb\udadc\udadd\udade\udadf\udae0\udae1\udae2\udae3\udae4\udae5\udae6\udae7\udae8\udae9\udaea\udaeb\udaec\udaed\udaee\udaef\udaf0\udaf1\udaf2\udaf3\udaf4\udaf5\udaf6\udaf7\udaf8\udaf9\udafa\udafb\udafc\udafd\udafe\udaff\udb00\udb01\udb02\udb03\udb04\udb05\udb06\udb07\udb08\udb09\udb0a\udb0b\udb0c\udb0d\udb0e\udb0f\udb10\udb11\udb12\udb13\udb14\udb15\udb16\udb17\udb18\udb19\udb1a\udb1b\udb1c\udb1d\udb1e\udb1f\udb20\udb21\udb22\udb23\udb24\udb25\udb26\udb27\udb28\udb29\udb2a\udb2b\udb2c\udb2d\udb2e\udb2f\udb30\udb31\udb32\udb33\udb34\udb35\udb36\udb37\udb38\udb39\udb3a\udb3b\udb3c\udb3d\udb3e\udb3f\udb40\udb41\udb42\udb43\udb44\udb45\udb46\udb47\udb48\udb49\udb4a\udb4b\udb4c\udb4d\udb4e\udb4f\udb50\udb51\udb52\udb53\udb54\udb55\udb56\udb57\udb58\udb59\udb5a\udb5b\udb5c\udb5d\udb5e\udb5f\udb60\udb61\udb62\udb63\udb64\udb65\udb66\udb67\udb68\udb69\udb6a\udb6b\udb6c\udb6d\udb6e\udb6f\udb70\udb71\udb72\udb73\udb74\udb75\udb76\udb77\udb78\udb79\udb7a\udb7b\udb7c\udb7d\udb7e\udb7f\udb80\udb81\udb82\udb83\udb84\udb85\udb86\udb87\udb88\udb89\udb8a\udb8b\udb8c\udb8d\udb8e\udb8f\udb90\udb91\udb92\udb93\udb94\udb95\udb96\udb97\udb98\udb99\udb9a\udb9b\udb9c\udb9d\udb9e\udb9f\udba0\udba1\udba2\udba3\udba4\udba5\udba6\udba7\udba8\udba9\udbaa\udbab\udbac\udbad\udbae\udbaf\udbb0\udbb1\udbb2\udbb3\udbb4\udbb5\udbb6\udbb7\udbb8\udbb9\udbba\udbbb\udbbc\udbbd\udbbe\udbbf\udbc0\udbc1\udbc2\udbc3\udbc4\udbc5\udbc6\udbc7\udbc8\udbc9\udbca\udbcb\udbcc\udbcd\udbce\udbcf\udbd0\udbd1\udbd2\udbd3\udbd4\udbd5\udbd6\udbd7\udbd8\udbd9\udbda\udbdb\udbdc\udbdd\udbde\udbdf\udbe0\udbe1\udbe2\udbe3\udbe4\udbe5\udbe6\udbe7\udbe8\udbe9\udbea\udbeb\udbec\udbed\udbee\udbef\udbf0\udbf1\udbf2\udbf3\udbf4\udbf5\udbf6\udbf7\udbf8\udbf9\udbfa\udbfb\udbfc\udbfd\udbfe\U0010fc00\udc01\udc02\udc03\udc04\udc05\udc06\udc07\udc08\udc09\udc0a\udc0b\udc0c\udc0d\udc0e\udc0f\udc10\udc11\udc12\udc13\udc14\udc15\udc16\udc17\udc18\udc19\udc1a\udc1b\udc1c\udc1d\udc1e\udc1f\udc20\udc21\udc22\udc23\udc24\udc25\udc26\udc27\udc28\udc29\udc2a\udc2b\udc2c\udc2d\udc2e\udc2f\udc30\udc31\udc32\udc33\udc34\udc35\udc36\udc37\udc38\udc39\udc3a\udc3b\udc3c\udc3d\udc3e\udc3f\udc40\udc41\udc42\udc43\udc44\udc45\udc46\udc47\udc48\udc49\udc4a\udc4b\udc4c\udc4d\udc4e\udc4f\udc50\udc51\udc52\udc53\udc54\udc55\udc56\udc57\udc58\udc59\udc5a\udc5b\udc5c\udc5d\udc5e\udc5f\udc60\udc61\udc62\udc63\udc64\udc65\udc66\udc67\udc68\udc69\udc6a\udc6b\udc6c\udc6d\udc6e\udc6f\udc70\udc71\udc72\udc73\udc74\udc75\udc76\udc77\udc78\udc79\udc7a\udc7b\udc7c\udc7d\udc7e\udc7f\udc80\udc81\udc82\udc83\udc84\udc85\udc86\udc87\udc88\udc89\udc8a\udc8b\udc8c\udc8d\udc8e\udc8f\udc90\udc91\udc92\udc93\udc94\udc95\udc96\udc97\udc98\udc99\udc9a\udc9b\udc9c\udc9d\udc9e\udc9f\udca0\udca1\udca2\udca3\udca4\udca5\udca6\udca7\udca8\udca9\udcaa\udcab\udcac\udcad\udcae\udcaf\udcb0\udcb1\udcb2\udcb3\udcb4\udcb5\udcb6\udcb7\udcb8\udcb9\udcba\udcbb\udcbc\udcbd\udcbe\udcbf\udcc0\udcc1\udcc2\udcc3\udcc4\udcc5\udcc6\udcc7\udcc8\udcc9\udcca\udccb\udccc\udccd\udcce\udccf\udcd0\udcd1\udcd2\udcd3\udcd4\udcd5\udcd6\udcd7\udcd8\udcd9\udcda\udcdb\udcdc\udcdd\udcde\udcdf\udce0\udce1\udce2\udce3\udce4\udce5\udce6\udce7\udce8\udce9\udcea\udceb\udcec\udced\udcee\udcef\udcf0\udcf1\udcf2\udcf3\udcf4\udcf5\udcf6\udcf7\udcf8\udcf9\udcfa\udcfb\udcfc\udcfd\udcfe\udcff\udd00\udd01\udd02\udd03\udd04\udd05\udd06\udd07\udd08\udd09\udd0a\udd0b\udd0c\udd0d\udd0e\udd0f\udd10\udd11\udd12\udd13\udd14\udd15\udd16\udd17\udd18\udd19\udd1a\udd1b\udd1c\udd1d\udd1e\udd1f\udd20\udd21\udd22\udd23\udd24\udd25\udd26\udd27\udd28\udd29\udd2a\udd2b\udd2c\udd2d\udd2e\udd2f\udd30\udd31\udd32\udd33\udd34\udd35\udd36\udd37\udd38\udd39\udd3a\udd3b\udd3c\udd3d\udd3e\udd3f\udd40\udd41\udd42\udd43\udd44\udd45\udd46\udd47\udd48\udd49\udd4a\udd4b\udd4c\udd4d\udd4e\udd4f\udd50\udd51\udd52\udd53\udd54\udd55\udd56\udd57\udd58\udd59\udd5a\udd5b\udd5c\udd5d\udd5e\udd5f\udd60\udd61\udd62\udd63\udd64\udd65\udd66\udd67\udd68\udd69\udd6a\udd6b\udd6c\udd6d\udd6e\udd6f\udd70\udd71\udd72\udd73\udd74\udd75\udd76\udd77\udd78\udd79\udd7a\udd7b\udd7c\udd7d\udd7e\udd7f\udd80\udd81\udd82\udd83\udd84\udd85\udd86\udd87\udd88\udd89\udd8a\udd8b\udd8c\udd8d\udd8e\udd8f\udd90\udd91\udd92\udd93\udd94\udd95\udd96\udd97\udd98\udd99\udd9a\udd9b\udd9c\udd9d\udd9e\udd9f\udda0\udda1\udda2\udda3\udda4\udda5\udda6\udda7\udda8\udda9\uddaa\uddab\uddac\uddad\uddae\uddaf\uddb0\uddb1\uddb2\uddb3\uddb4\uddb5\uddb6\uddb7\uddb8\uddb9\uddba\uddbb\uddbc\uddbd\uddbe\uddbf\uddc0\uddc1\uddc2\uddc3\uddc4\uddc5\uddc6\uddc7\uddc8\uddc9\uddca\uddcb\uddcc\uddcd\uddce\uddcf\uddd0\uddd1\uddd2\uddd3\uddd4\uddd5\uddd6\uddd7\uddd8\uddd9\uddda\udddb\udddc\udddd\uddde\udddf\udde0\udde1\udde2\udde3\udde4\udde5\udde6\udde7\udde8\udde9\uddea\uddeb\uddec\udded\uddee\uddef\uddf0\uddf1\uddf2\uddf3\uddf4\uddf5\uddf6\uddf7\uddf8\uddf9\uddfa\uddfb\uddfc\uddfd\uddfe\uddff\ude00\ude01\ude02\ude03\ude04\ude05\ude06\ude07\ude08\ude09\ude0a\ude0b\ude0c\ude0d\ude0e\ude0f\ude10\ude11\ude12\ude13\ude14\ude15\ude16\ude17\ude18\ude19\ude1a\ude1b\ude1c\ude1d\ude1e\ude1f\ude20\ude21\ude22\ude23\ude24\ude25\ude26\ude27\ude28\ude29\ude2a\ude2b\ude2c\ude2d\ude2e\ude2f\ude30\ude31\ude32\ude33\ude34\ude35\ude36\ude37\ude38\ude39\ude3a\ude3b\ude3c\ude3d\ude3e\ude3f\ude40\ude41\ude42\ude43\ude44\ude45\ude46\ude47\ude48\ude49\ude4a\ude4b\ude4c\ude4d\ude4e\ude4f\ude50\ude51\ude52\ude53\ude54\ude55\ude56\ude57\ude58\ude59\ude5a\ude5b\ude5c\ude5d\ude5e\ude5f\ude60\ude61\ude62\ude63\ude64\ude65\ude66\ude67\ude68\ude69\ude6a\ude6b\ude6c\ude6d\ude6e\ude6f\ude70\ude71\ude72\ude73\ude74\ude75\ude76\ude77\ude78\ude79\ude7a\ude7b\ude7c\ude7d\ude7e\ude7f\ude80\ude81\ude82\ude83\ude84\ude85\ude86\ude87\ude88\ude89\ude8a\ude8b\ude8c\ude8d\ude8e\ude8f\ude90\ude91\ude92\ude93\ude94\ude95\ude96\ude97\ude98\ude99\ude9a\ude9b\ude9c\ude9d\ude9e\ude9f\udea0\udea1\udea2\udea3\udea4\udea5\udea6\udea7\udea8\udea9\udeaa\udeab\udeac\udead\udeae\udeaf\udeb0\udeb1\udeb2\udeb3\udeb4\udeb5\udeb6\udeb7\udeb8\udeb9\udeba\udebb\udebc\udebd\udebe\udebf\udec0\udec1\udec2\udec3\udec4\udec5\udec6\udec7\udec8\udec9\udeca\udecb\udecc\udecd\udece\udecf\uded0\uded1\uded2\uded3\uded4\uded5\uded6\uded7\uded8\uded9\udeda\udedb\udedc\udedd\udede\udedf\udee0\udee1\udee2\udee3\udee4\udee5\udee6\udee7\udee8\udee9\udeea\udeeb\udeec\udeed\udeee\udeef\udef0\udef1\udef2\udef3\udef4\udef5\udef6\udef7\udef8\udef9\udefa\udefb\udefc\udefd\udefe\udeff\udf00\udf01\udf02\udf03\udf04\udf05\udf06\udf07\udf08\udf09\udf0a\udf0b\udf0c\udf0d\udf0e\udf0f\udf10\udf11\udf12\udf13\udf14\udf15\udf16\udf17\udf18\udf19\udf1a\udf1b\udf1c\udf1d\udf1e\udf1f\udf20\udf21\udf22\udf23\udf24\udf25\udf26\udf27\udf28\udf29\udf2a\udf2b\udf2c\udf2d\udf2e\udf2f\udf30\udf31\udf32\udf33\udf34\udf35\udf36\udf37\udf38\udf39\udf3a\udf3b\udf3c\udf3d\udf3e\udf3f\udf40\udf41\udf42\udf43\udf44\udf45\udf46\udf47\udf48\udf49\udf4a\udf4b\udf4c\udf4d\udf4e\udf4f\udf50\udf51\udf52\udf53\udf54\udf55\udf56\udf57\udf58\udf59\udf5a\udf5b\udf5c\udf5d\udf5e\udf5f\udf60\udf61\udf62\udf63\udf64\udf65\udf66\udf67\udf68\udf69\udf6a\udf6b\udf6c\udf6d\udf6e\udf6f\udf70\udf71\udf72\udf73\udf74\udf75\udf76\udf77\udf78\udf79\udf7a\udf7b\udf7c\udf7d\udf7e\udf7f\udf80\udf81\udf82\udf83\udf84\udf85\udf86\udf87\udf88\udf89\udf8a\udf8b\udf8c\udf8d\udf8e\udf8f\udf90\udf91\udf92\udf93\udf94\udf95\udf96\udf97\udf98\udf99\udf9a\udf9b\udf9c\udf9d\udf9e\udf9f\udfa0\udfa1\udfa2\udfa3\udfa4\udfa5\udfa6\udfa7\udfa8\udfa9\udfaa\udfab\udfac\udfad\udfae\udfaf\udfb0\udfb1\udfb2\udfb3\udfb4\udfb5\udfb6\udfb7\udfb8\udfb9\udfba\udfbb\udfbc\udfbd\udfbe\udfbf\udfc0\udfc1\udfc2\udfc3\udfc4\udfc5\udfc6\udfc7\udfc8\udfc9\udfca\udfcb\udfcc\udfcd\udfce\udfcf\udfd0\udfd1\udfd2\udfd3\udfd4\udfd5\udfd6\udfd7\udfd8\udfd9\udfda\udfdb\udfdc\udfdd\udfde\udfdf\udfe0\udfe1\udfe2\udfe3\udfe4\udfe5\udfe6\udfe7\udfe8\udfe9\udfea\udfeb\udfec\udfed\udfee\udfef\udff0\udff1\udff2\udff3\udff4\udff5\udff6\udff7\udff8\udff9\udffa\udffb\udffc\udffd\udffe\udfff'") +except UnicodeDecodeError: + Cs = '' # Jython can't handle isolated surrogates + +Ll = u'abcdefghijklmnopqrstuvwxyz\xaa\xb5\xba\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff\u0101\u0103\u0105\u0107\u0109\u010b\u010d\u010f\u0111\u0113\u0115\u0117\u0119\u011b\u011d\u011f\u0121\u0123\u0125\u0127\u0129\u012b\u012d\u012f\u0131\u0133\u0135\u0137\u0138\u013a\u013c\u013e\u0140\u0142\u0144\u0146\u0148\u0149\u014b\u014d\u014f\u0151\u0153\u0155\u0157\u0159\u015b\u015d\u015f\u0161\u0163\u0165\u0167\u0169\u016b\u016d\u016f\u0171\u0173\u0175\u0177\u017a\u017c\u017e\u017f\u0180\u0183\u0185\u0188\u018c\u018d\u0192\u0195\u0199\u019a\u019b\u019e\u01a1\u01a3\u01a5\u01a8\u01aa\u01ab\u01ad\u01b0\u01b4\u01b6\u01b9\u01ba\u01bd\u01be\u01bf\u01c6\u01c9\u01cc\u01ce\u01d0\u01d2\u01d4\u01d6\u01d8\u01da\u01dc\u01dd\u01df\u01e1\u01e3\u01e5\u01e7\u01e9\u01eb\u01ed\u01ef\u01f0\u01f3\u01f5\u01f9\u01fb\u01fd\u01ff\u0201\u0203\u0205\u0207\u0209\u020b\u020d\u020f\u0211\u0213\u0215\u0217\u0219\u021b\u021d\u021f\u0221\u0223\u0225\u0227\u0229\u022b\u022d\u022f\u0231\u0233\u0234\u0235\u0236\u0237\u0238\u0239\u023c\u023f\u0240\u0250\u0251\u0252\u0253\u0254\u0255\u0256\u0257\u0258\u0259\u025a\u025b\u025c\u025d\u025e\u025f\u0260\u0261\u0262\u0263\u0264\u0265\u0266\u0267\u0268\u0269\u026a\u026b\u026c\u026d\u026e\u026f\u0270\u0271\u0272\u0273\u0274\u0275\u0276\u0277\u0278\u0279\u027a\u027b\u027c\u027d\u027e\u027f\u0280\u0281\u0282\u0283\u0284\u0285\u0286\u0287\u0288\u0289\u028a\u028b\u028c\u028d\u028e\u028f\u0290\u0291\u0292\u0293\u0294\u0295\u0296\u0297\u0298\u0299\u029a\u029b\u029c\u029d\u029e\u029f\u02a0\u02a1\u02a2\u02a3\u02a4\u02a5\u02a6\u02a7\u02a8\u02a9\u02aa\u02ab\u02ac\u02ad\u02ae\u02af\u0390\u03ac\u03ad\u03ae\u03af\u03b0\u03b1\u03b2\u03b3\u03b4\u03b5\u03b6\u03b7\u03b8\u03b9\u03ba\u03bb\u03bc\u03bd\u03be\u03bf\u03c0\u03c1\u03c2\u03c3\u03c4\u03c5\u03c6\u03c7\u03c8\u03c9\u03ca\u03cb\u03cc\u03cd\u03ce\u03d0\u03d1\u03d5\u03d6\u03d7\u03d9\u03db\u03dd\u03df\u03e1\u03e3\u03e5\u03e7\u03e9\u03eb\u03ed\u03ef\u03f0\u03f1\u03f2\u03f3\u03f5\u03f8\u03fb\u03fc\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043a\u043b\u043c\u043d\u043e\u043f\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044a\u044b\u044c\u044d\u044e\u044f\u0450\u0451\u0452\u0453\u0454\u0455\u0456\u0457\u0458\u0459\u045a\u045b\u045c\u045d\u045e\u045f\u0461\u0463\u0465\u0467\u0469\u046b\u046d\u046f\u0471\u0473\u0475\u0477\u0479\u047b\u047d\u047f\u0481\u048b\u048d\u048f\u0491\u0493\u0495\u0497\u0499\u049b\u049d\u049f\u04a1\u04a3\u04a5\u04a7\u04a9\u04ab\u04ad\u04af\u04b1\u04b3\u04b5\u04b7\u04b9\u04bb\u04bd\u04bf\u04c2\u04c4\u04c6\u04c8\u04ca\u04cc\u04ce\u04d1\u04d3\u04d5\u04d7\u04d9\u04db\u04dd\u04df\u04e1\u04e3\u04e5\u04e7\u04e9\u04eb\u04ed\u04ef\u04f1\u04f3\u04f5\u04f7\u04f9\u0501\u0503\u0505\u0507\u0509\u050b\u050d\u050f\u0561\u0562\u0563\u0564\u0565\u0566\u0567\u0568\u0569\u056a\u056b\u056c\u056d\u056e\u056f\u0570\u0571\u0572\u0573\u0574\u0575\u0576\u0577\u0578\u0579\u057a\u057b\u057c\u057d\u057e\u057f\u0580\u0581\u0582\u0583\u0584\u0585\u0586\u0587\u1d00\u1d01\u1d02\u1d03\u1d04\u1d05\u1d06\u1d07\u1d08\u1d09\u1d0a\u1d0b\u1d0c\u1d0d\u1d0e\u1d0f\u1d10\u1d11\u1d12\u1d13\u1d14\u1d15\u1d16\u1d17\u1d18\u1d19\u1d1a\u1d1b\u1d1c\u1d1d\u1d1e\u1d1f\u1d20\u1d21\u1d22\u1d23\u1d24\u1d25\u1d26\u1d27\u1d28\u1d29\u1d2a\u1d2b\u1d62\u1d63\u1d64\u1d65\u1d66\u1d67\u1d68\u1d69\u1d6a\u1d6b\u1d6c\u1d6d\u1d6e\u1d6f\u1d70\u1d71\u1d72\u1d73\u1d74\u1d75\u1d76\u1d77\u1d79\u1d7a\u1d7b\u1d7c\u1d7d\u1d7e\u1d7f\u1d80\u1d81\u1d82\u1d83\u1d84\u1d85\u1d86\u1d87\u1d88\u1d89\u1d8a\u1d8b\u1d8c\u1d8d\u1d8e\u1d8f\u1d90\u1d91\u1d92\u1d93\u1d94\u1d95\u1d96\u1d97\u1d98\u1d99\u1d9a\u1e01\u1e03\u1e05\u1e07\u1e09\u1e0b\u1e0d\u1e0f\u1e11\u1e13\u1e15\u1e17\u1e19\u1e1b\u1e1d\u1e1f\u1e21\u1e23\u1e25\u1e27\u1e29\u1e2b\u1e2d\u1e2f\u1e31\u1e33\u1e35\u1e37\u1e39\u1e3b\u1e3d\u1e3f\u1e41\u1e43\u1e45\u1e47\u1e49\u1e4b\u1e4d\u1e4f\u1e51\u1e53\u1e55\u1e57\u1e59\u1e5b\u1e5d\u1e5f\u1e61\u1e63\u1e65\u1e67\u1e69\u1e6b\u1e6d\u1e6f\u1e71\u1e73\u1e75\u1e77\u1e79\u1e7b\u1e7d\u1e7f\u1e81\u1e83\u1e85\u1e87\u1e89\u1e8b\u1e8d\u1e8f\u1e91\u1e93\u1e95\u1e96\u1e97\u1e98\u1e99\u1e9a\u1e9b\u1ea1\u1ea3\u1ea5\u1ea7\u1ea9\u1eab\u1ead\u1eaf\u1eb1\u1eb3\u1eb5\u1eb7\u1eb9\u1ebb\u1ebd\u1ebf\u1ec1\u1ec3\u1ec5\u1ec7\u1ec9\u1ecb\u1ecd\u1ecf\u1ed1\u1ed3\u1ed5\u1ed7\u1ed9\u1edb\u1edd\u1edf\u1ee1\u1ee3\u1ee5\u1ee7\u1ee9\u1eeb\u1eed\u1eef\u1ef1\u1ef3\u1ef5\u1ef7\u1ef9\u1f00\u1f01\u1f02\u1f03\u1f04\u1f05\u1f06\u1f07\u1f10\u1f11\u1f12\u1f13\u1f14\u1f15\u1f20\u1f21\u1f22\u1f23\u1f24\u1f25\u1f26\u1f27\u1f30\u1f31\u1f32\u1f33\u1f34\u1f35\u1f36\u1f37\u1f40\u1f41\u1f42\u1f43\u1f44\u1f45\u1f50\u1f51\u1f52\u1f53\u1f54\u1f55\u1f56\u1f57\u1f60\u1f61\u1f62\u1f63\u1f64\u1f65\u1f66\u1f67\u1f70\u1f71\u1f72\u1f73\u1f74\u1f75\u1f76\u1f77\u1f78\u1f79\u1f7a\u1f7b\u1f7c\u1f7d\u1f80\u1f81\u1f82\u1f83\u1f84\u1f85\u1f86\u1f87\u1f90\u1f91\u1f92\u1f93\u1f94\u1f95\u1f96\u1f97\u1fa0\u1fa1\u1fa2\u1fa3\u1fa4\u1fa5\u1fa6\u1fa7\u1fb0\u1fb1\u1fb2\u1fb3\u1fb4\u1fb6\u1fb7\u1fbe\u1fc2\u1fc3\u1fc4\u1fc6\u1fc7\u1fd0\u1fd1\u1fd2\u1fd3\u1fd6\u1fd7\u1fe0\u1fe1\u1fe2\u1fe3\u1fe4\u1fe5\u1fe6\u1fe7\u1ff2\u1ff3\u1ff4\u1ff6\u1ff7\u2071\u207f\u210a\u210e\u210f\u2113\u212f\u2134\u2139\u213c\u213d\u2146\u2147\u2148\u2149\u2c30\u2c31\u2c32\u2c33\u2c34\u2c35\u2c36\u2c37\u2c38\u2c39\u2c3a\u2c3b\u2c3c\u2c3d\u2c3e\u2c3f\u2c40\u2c41\u2c42\u2c43\u2c44\u2c45\u2c46\u2c47\u2c48\u2c49\u2c4a\u2c4b\u2c4c\u2c4d\u2c4e\u2c4f\u2c50\u2c51\u2c52\u2c53\u2c54\u2c55\u2c56\u2c57\u2c58\u2c59\u2c5a\u2c5b\u2c5c\u2c5d\u2c5e\u2c81\u2c83\u2c85\u2c87\u2c89\u2c8b\u2c8d\u2c8f\u2c91\u2c93\u2c95\u2c97\u2c99\u2c9b\u2c9d\u2c9f\u2ca1\u2ca3\u2ca5\u2ca7\u2ca9\u2cab\u2cad\u2caf\u2cb1\u2cb3\u2cb5\u2cb7\u2cb9\u2cbb\u2cbd\u2cbf\u2cc1\u2cc3\u2cc5\u2cc7\u2cc9\u2ccb\u2ccd\u2ccf\u2cd1\u2cd3\u2cd5\u2cd7\u2cd9\u2cdb\u2cdd\u2cdf\u2ce1\u2ce3\u2ce4\u2d00\u2d01\u2d02\u2d03\u2d04\u2d05\u2d06\u2d07\u2d08\u2d09\u2d0a\u2d0b\u2d0c\u2d0d\u2d0e\u2d0f\u2d10\u2d11\u2d12\u2d13\u2d14\u2d15\u2d16\u2d17\u2d18\u2d19\u2d1a\u2d1b\u2d1c\u2d1d\u2d1e\u2d1f\u2d20\u2d21\u2d22\u2d23\u2d24\u2d25\ufb00\ufb01\ufb02\ufb03\ufb04\ufb05\ufb06\ufb13\ufb14\ufb15\ufb16\ufb17\uff41\uff42\uff43\uff44\uff45\uff46\uff47\uff48\uff49\uff4a\uff4b\uff4c\uff4d\uff4e\uff4f\uff50\uff51\uff52\uff53\uff54\uff55\uff56\uff57\uff58\uff59\uff5a' + +Lm = u'\u02b0\u02b1\u02b2\u02b3\u02b4\u02b5\u02b6\u02b7\u02b8\u02b9\u02ba\u02bb\u02bc\u02bd\u02be\u02bf\u02c0\u02c1\u02c6\u02c7\u02c8\u02c9\u02ca\u02cb\u02cc\u02cd\u02ce\u02cf\u02d0\u02d1\u02e0\u02e1\u02e2\u02e3\u02e4\u02ee\u037a\u0559\u0640\u06e5\u06e6\u0e46\u0ec6\u10fc\u17d7\u1843\u1d2c\u1d2d\u1d2e\u1d2f\u1d30\u1d31\u1d32\u1d33\u1d34\u1d35\u1d36\u1d37\u1d38\u1d39\u1d3a\u1d3b\u1d3c\u1d3d\u1d3e\u1d3f\u1d40\u1d41\u1d42\u1d43\u1d44\u1d45\u1d46\u1d47\u1d48\u1d49\u1d4a\u1d4b\u1d4c\u1d4d\u1d4e\u1d4f\u1d50\u1d51\u1d52\u1d53\u1d54\u1d55\u1d56\u1d57\u1d58\u1d59\u1d5a\u1d5b\u1d5c\u1d5d\u1d5e\u1d5f\u1d60\u1d61\u1d78\u1d9b\u1d9c\u1d9d\u1d9e\u1d9f\u1da0\u1da1\u1da2\u1da3\u1da4\u1da5\u1da6\u1da7\u1da8\u1da9\u1daa\u1dab\u1dac\u1dad\u1dae\u1daf\u1db0\u1db1\u1db2\u1db3\u1db4\u1db5\u1db6\u1db7\u1db8\u1db9\u1dba\u1dbb\u1dbc\u1dbd\u1dbe\u1dbf\u2090\u2091\u2092\u2093\u2094\u2d6f\u3005\u3031\u3032\u3033\u3034\u3035\u303b\u309d\u309e\u30fc\u30fd\u30fe\ua015\uff70\uff9e\uff9f' + +Lo = u'\u01bb\u01c0\u01c1\u01c2\u01c3\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9\u05da\u05db\u05dc\u05dd\u05de\u05df\u05e0\u05e1\u05e2\u05e3\u05e4\u05e5\u05e6\u05e7\u05e8\u05e9\u05ea\u05f0\u05f1\u05f2\u0621\u0622\u0623\u0624\u0625\u0626\u0627\u0628\u0629\u062a\u062b\u062c\u062d\u062e\u062f\u0630\u0631\u0632\u0633\u0634\u0635\u0636\u0637\u0638\u0639\u063a\u0641\u0642\u0643\u0644\u0645\u0646\u0647\u0648\u0649\u064a\u066e\u066f\u0671\u0672\u0673\u0674\u0675\u0676\u0677\u0678\u0679\u067a\u067b\u067c\u067d\u067e\u067f\u0680\u0681\u0682\u0683\u0684\u0685\u0686\u0687\u0688\u0689\u068a\u068b\u068c\u068d\u068e\u068f\u0690\u0691\u0692\u0693\u0694\u0695\u0696\u0697\u0698\u0699\u069a\u069b\u069c\u069d\u069e\u069f\u06a0\u06a1\u06a2\u06a3\u06a4\u06a5\u06a6\u06a7\u06a8\u06a9\u06aa\u06ab\u06ac\u06ad\u06ae\u06af\u06b0\u06b1\u06b2\u06b3\u06b4\u06b5\u06b6\u06b7\u06b8\u06b9\u06ba\u06bb\u06bc\u06bd\u06be\u06bf\u06c0\u06c1\u06c2\u06c3\u06c4\u06c5\u06c6\u06c7\u06c8\u06c9\u06ca\u06cb\u06cc\u06cd\u06ce\u06cf\u06d0\u06d1\u06d2\u06d3\u06d5\u06ee\u06ef\u06fa\u06fb\u06fc\u06ff\u0710\u0712\u0713\u0714\u0715\u0716\u0717\u0718\u0719\u071a\u071b\u071c\u071d\u071e\u071f\u0720\u0721\u0722\u0723\u0724\u0725\u0726\u0727\u0728\u0729\u072a\u072b\u072c\u072d\u072e\u072f\u074d\u074e\u074f\u0750\u0751\u0752\u0753\u0754\u0755\u0756\u0757\u0758\u0759\u075a\u075b\u075c\u075d\u075e\u075f\u0760\u0761\u0762\u0763\u0764\u0765\u0766\u0767\u0768\u0769\u076a\u076b\u076c\u076d\u0780\u0781\u0782\u0783\u0784\u0785\u0786\u0787\u0788\u0789\u078a\u078b\u078c\u078d\u078e\u078f\u0790\u0791\u0792\u0793\u0794\u0795\u0796\u0797\u0798\u0799\u079a\u079b\u079c\u079d\u079e\u079f\u07a0\u07a1\u07a2\u07a3\u07a4\u07a5\u07b1\u0904\u0905\u0906\u0907\u0908\u0909\u090a\u090b\u090c\u090d\u090e\u090f\u0910\u0911\u0912\u0913\u0914\u0915\u0916\u0917\u0918\u0919\u091a\u091b\u091c\u091d\u091e\u091f\u0920\u0921\u0922\u0923\u0924\u0925\u0926\u0927\u0928\u0929\u092a\u092b\u092c\u092d\u092e\u092f\u0930\u0931\u0932\u0933\u0934\u0935\u0936\u0937\u0938\u0939\u093d\u0950\u0958\u0959\u095a\u095b\u095c\u095d\u095e\u095f\u0960\u0961\u097d\u0985\u0986\u0987\u0988\u0989\u098a\u098b\u098c\u098f\u0990\u0993\u0994\u0995\u0996\u0997\u0998\u0999\u099a\u099b\u099c\u099d\u099e\u099f\u09a0\u09a1\u09a2\u09a3\u09a4\u09a5\u09a6\u09a7\u09a8\u09aa\u09ab\u09ac\u09ad\u09ae\u09af\u09b0\u09b2\u09b6\u09b7\u09b8\u09b9\u09bd\u09ce\u09dc\u09dd\u09df\u09e0\u09e1\u09f0\u09f1\u0a05\u0a06\u0a07\u0a08\u0a09\u0a0a\u0a0f\u0a10\u0a13\u0a14\u0a15\u0a16\u0a17\u0a18\u0a19\u0a1a\u0a1b\u0a1c\u0a1d\u0a1e\u0a1f\u0a20\u0a21\u0a22\u0a23\u0a24\u0a25\u0a26\u0a27\u0a28\u0a2a\u0a2b\u0a2c\u0a2d\u0a2e\u0a2f\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59\u0a5a\u0a5b\u0a5c\u0a5e\u0a72\u0a73\u0a74\u0a85\u0a86\u0a87\u0a88\u0a89\u0a8a\u0a8b\u0a8c\u0a8d\u0a8f\u0a90\u0a91\u0a93\u0a94\u0a95\u0a96\u0a97\u0a98\u0a99\u0a9a\u0a9b\u0a9c\u0a9d\u0a9e\u0a9f\u0aa0\u0aa1\u0aa2\u0aa3\u0aa4\u0aa5\u0aa6\u0aa7\u0aa8\u0aaa\u0aab\u0aac\u0aad\u0aae\u0aaf\u0ab0\u0ab2\u0ab3\u0ab5\u0ab6\u0ab7\u0ab8\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05\u0b06\u0b07\u0b08\u0b09\u0b0a\u0b0b\u0b0c\u0b0f\u0b10\u0b13\u0b14\u0b15\u0b16\u0b17\u0b18\u0b19\u0b1a\u0b1b\u0b1c\u0b1d\u0b1e\u0b1f\u0b20\u0b21\u0b22\u0b23\u0b24\u0b25\u0b26\u0b27\u0b28\u0b2a\u0b2b\u0b2c\u0b2d\u0b2e\u0b2f\u0b30\u0b32\u0b33\u0b35\u0b36\u0b37\u0b38\u0b39\u0b3d\u0b5c\u0b5d\u0b5f\u0b60\u0b61\u0b71\u0b83\u0b85\u0b86\u0b87\u0b88\u0b89\u0b8a\u0b8e\u0b8f\u0b90\u0b92\u0b93\u0b94\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8\u0ba9\u0baa\u0bae\u0baf\u0bb0\u0bb1\u0bb2\u0bb3\u0bb4\u0bb5\u0bb6\u0bb7\u0bb8\u0bb9\u0c05\u0c06\u0c07\u0c08\u0c09\u0c0a\u0c0b\u0c0c\u0c0e\u0c0f\u0c10\u0c12\u0c13\u0c14\u0c15\u0c16\u0c17\u0c18\u0c19\u0c1a\u0c1b\u0c1c\u0c1d\u0c1e\u0c1f\u0c20\u0c21\u0c22\u0c23\u0c24\u0c25\u0c26\u0c27\u0c28\u0c2a\u0c2b\u0c2c\u0c2d\u0c2e\u0c2f\u0c30\u0c31\u0c32\u0c33\u0c35\u0c36\u0c37\u0c38\u0c39\u0c60\u0c61\u0c85\u0c86\u0c87\u0c88\u0c89\u0c8a\u0c8b\u0c8c\u0c8e\u0c8f\u0c90\u0c92\u0c93\u0c94\u0c95\u0c96\u0c97\u0c98\u0c99\u0c9a\u0c9b\u0c9c\u0c9d\u0c9e\u0c9f\u0ca0\u0ca1\u0ca2\u0ca3\u0ca4\u0ca5\u0ca6\u0ca7\u0ca8\u0caa\u0cab\u0cac\u0cad\u0cae\u0caf\u0cb0\u0cb1\u0cb2\u0cb3\u0cb5\u0cb6\u0cb7\u0cb8\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0d05\u0d06\u0d07\u0d08\u0d09\u0d0a\u0d0b\u0d0c\u0d0e\u0d0f\u0d10\u0d12\u0d13\u0d14\u0d15\u0d16\u0d17\u0d18\u0d19\u0d1a\u0d1b\u0d1c\u0d1d\u0d1e\u0d1f\u0d20\u0d21\u0d22\u0d23\u0d24\u0d25\u0d26\u0d27\u0d28\u0d2a\u0d2b\u0d2c\u0d2d\u0d2e\u0d2f\u0d30\u0d31\u0d32\u0d33\u0d34\u0d35\u0d36\u0d37\u0d38\u0d39\u0d60\u0d61\u0d85\u0d86\u0d87\u0d88\u0d89\u0d8a\u0d8b\u0d8c\u0d8d\u0d8e\u0d8f\u0d90\u0d91\u0d92\u0d93\u0d94\u0d95\u0d96\u0d9a\u0d9b\u0d9c\u0d9d\u0d9e\u0d9f\u0da0\u0da1\u0da2\u0da3\u0da4\u0da5\u0da6\u0da7\u0da8\u0da9\u0daa\u0dab\u0dac\u0dad\u0dae\u0daf\u0db0\u0db1\u0db3\u0db4\u0db5\u0db6\u0db7\u0db8\u0db9\u0dba\u0dbb\u0dbd\u0dc0\u0dc1\u0dc2\u0dc3\u0dc4\u0dc5\u0dc6\u0e01\u0e02\u0e03\u0e04\u0e05\u0e06\u0e07\u0e08\u0e09\u0e0a\u0e0b\u0e0c\u0e0d\u0e0e\u0e0f\u0e10\u0e11\u0e12\u0e13\u0e14\u0e15\u0e16\u0e17\u0e18\u0e19\u0e1a\u0e1b\u0e1c\u0e1d\u0e1e\u0e1f\u0e20\u0e21\u0e22\u0e23\u0e24\u0e25\u0e26\u0e27\u0e28\u0e29\u0e2a\u0e2b\u0e2c\u0e2d\u0e2e\u0e2f\u0e30\u0e32\u0e33\u0e40\u0e41\u0e42\u0e43\u0e44\u0e45\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94\u0e95\u0e96\u0e97\u0e99\u0e9a\u0e9b\u0e9c\u0e9d\u0e9e\u0e9f\u0ea1\u0ea2\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead\u0eae\u0eaf\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0\u0ec1\u0ec2\u0ec3\u0ec4\u0edc\u0edd\u0f00\u0f40\u0f41\u0f42\u0f43\u0f44\u0f45\u0f46\u0f47\u0f49\u0f4a\u0f4b\u0f4c\u0f4d\u0f4e\u0f4f\u0f50\u0f51\u0f52\u0f53\u0f54\u0f55\u0f56\u0f57\u0f58\u0f59\u0f5a\u0f5b\u0f5c\u0f5d\u0f5e\u0f5f\u0f60\u0f61\u0f62\u0f63\u0f64\u0f65\u0f66\u0f67\u0f68\u0f69\u0f6a\u0f88\u0f89\u0f8a\u0f8b\u1000\u1001\u1002\u1003\u1004\u1005\u1006\u1007\u1008\u1009\u100a\u100b\u100c\u100d\u100e\u100f\u1010\u1011\u1012\u1013\u1014\u1015\u1016\u1017\u1018\u1019\u101a\u101b\u101c\u101d\u101e\u101f\u1020\u1021\u1023\u1024\u1025\u1026\u1027\u1029\u102a\u1050\u1051\u1052\u1053\u1054\u1055\u10d0\u10d1\u10d2\u10d3\u10d4\u10d5\u10d6\u10d7\u10d8\u10d9\u10da\u10db\u10dc\u10dd\u10de\u10df\u10e0\u10e1\u10e2\u10e3\u10e4\u10e5\u10e6\u10e7\u10e8\u10e9\u10ea\u10eb\u10ec\u10ed\u10ee\u10ef\u10f0\u10f1\u10f2\u10f3\u10f4\u10f5\u10f6\u10f7\u10f8\u10f9\u10fa\u1100\u1101\u1102\u1103\u1104\u1105\u1106\u1107\u1108\u1109\u110a\u110b\u110c\u110d\u110e\u110f\u1110\u1111\u1112\u1113\u1114\u1115\u1116\u1117\u1118\u1119\u111a\u111b\u111c\u111d\u111e\u111f\u1120\u1121\u1122\u1123\u1124\u1125\u1126\u1127\u1128\u1129\u112a\u112b\u112c\u112d\u112e\u112f\u1130\u1131\u1132\u1133\u1134\u1135\u1136\u1137\u1138\u1139\u113a\u113b\u113c\u113d\u113e\u113f\u1140\u1141\u1142\u1143\u1144\u1145\u1146\u1147\u1148\u1149\u114a\u114b\u114c\u114d\u114e\u114f\u1150\u1151\u1152\u1153\u1154\u1155\u1156\u1157\u1158\u1159\u115f\u1160\u1161\u1162\u1163\u1164\u1165\u1166\u1167\u1168\u1169\u116a\u116b\u116c\u116d\u116e\u116f\u1170\u1171\u1172\u1173\u1174\u1175\u1176\u1177\u1178\u1179\u117a\u117b\u117c\u117d\u117e\u117f\u1180\u1181\u1182\u1183\u1184\u1185\u1186\u1187\u1188\u1189\u118a\u118b\u118c\u118d\u118e\u118f\u1190\u1191\u1192\u1193\u1194\u1195\u1196\u1197\u1198\u1199\u119a\u119b\u119c\u119d\u119e\u119f\u11a0\u11a1\u11a2\u11a8\u11a9\u11aa\u11ab\u11ac\u11ad\u11ae\u11af\u11b0\u11b1\u11b2\u11b3\u11b4\u11b5\u11b6\u11b7\u11b8\u11b9\u11ba\u11bb\u11bc\u11bd\u11be\u11bf\u11c0\u11c1\u11c2\u11c3\u11c4\u11c5\u11c6\u11c7\u11c8\u11c9\u11ca\u11cb\u11cc\u11cd\u11ce\u11cf\u11d0\u11d1\u11d2\u11d3\u11d4\u11d5\u11d6\u11d7\u11d8\u11d9\u11da\u11db\u11dc\u11dd\u11de\u11df\u11e0\u11e1\u11e2\u11e3\u11e4\u11e5\u11e6\u11e7\u11e8\u11e9\u11ea\u11eb\u11ec\u11ed\u11ee\u11ef\u11f0\u11f1\u11f2\u11f3\u11f4\u11f5\u11f6\u11f7\u11f8\u11f9\u1200\u1201\u1202\u1203\u1204\u1205\u1206\u1207\u1208\u1209\u120a\u120b\u120c\u120d\u120e\u120f\u1210\u1211\u1212\u1213\u1214\u1215\u1216\u1217\u1218\u1219\u121a\u121b\u121c\u121d\u121e\u121f\u1220\u1221\u1222\u1223\u1224\u1225\u1226\u1227\u1228\u1229\u122a\u122b\u122c\u122d\u122e\u122f\u1230\u1231\u1232\u1233\u1234\u1235\u1236\u1237\u1238\u1239\u123a\u123b\u123c\u123d\u123e\u123f\u1240\u1241\u1242\u1243\u1244\u1245\u1246\u1247\u1248\u124a\u124b\u124c\u124d\u1250\u1251\u1252\u1253\u1254\u1255\u1256\u1258\u125a\u125b\u125c\u125d\u1260\u1261\u1262\u1263\u1264\u1265\u1266\u1267\u1268\u1269\u126a\u126b\u126c\u126d\u126e\u126f\u1270\u1271\u1272\u1273\u1274\u1275\u1276\u1277\u1278\u1279\u127a\u127b\u127c\u127d\u127e\u127f\u1280\u1281\u1282\u1283\u1284\u1285\u1286\u1287\u1288\u128a\u128b\u128c\u128d\u1290\u1291\u1292\u1293\u1294\u1295\u1296\u1297\u1298\u1299\u129a\u129b\u129c\u129d\u129e\u129f\u12a0\u12a1\u12a2\u12a3\u12a4\u12a5\u12a6\u12a7\u12a8\u12a9\u12aa\u12ab\u12ac\u12ad\u12ae\u12af\u12b0\u12b2\u12b3\u12b4\u12b5\u12b8\u12b9\u12ba\u12bb\u12bc\u12bd\u12be\u12c0\u12c2\u12c3\u12c4\u12c5\u12c8\u12c9\u12ca\u12cb\u12cc\u12cd\u12ce\u12cf\u12d0\u12d1\u12d2\u12d3\u12d4\u12d5\u12d6\u12d8\u12d9\u12da\u12db\u12dc\u12dd\u12de\u12df\u12e0\u12e1\u12e2\u12e3\u12e4\u12e5\u12e6\u12e7\u12e8\u12e9\u12ea\u12eb\u12ec\u12ed\u12ee\u12ef\u12f0\u12f1\u12f2\u12f3\u12f4\u12f5\u12f6\u12f7\u12f8\u12f9\u12fa\u12fb\u12fc\u12fd\u12fe\u12ff\u1300\u1301\u1302\u1303\u1304\u1305\u1306\u1307\u1308\u1309\u130a\u130b\u130c\u130d\u130e\u130f\u1310\u1312\u1313\u1314\u1315\u1318\u1319\u131a\u131b\u131c\u131d\u131e\u131f\u1320\u1321\u1322\u1323\u1324\u1325\u1326\u1327\u1328\u1329\u132a\u132b\u132c\u132d\u132e\u132f\u1330\u1331\u1332\u1333\u1334\u1335\u1336\u1337\u1338\u1339\u133a\u133b\u133c\u133d\u133e\u133f\u1340\u1341\u1342\u1343\u1344\u1345\u1346\u1347\u1348\u1349\u134a\u134b\u134c\u134d\u134e\u134f\u1350\u1351\u1352\u1353\u1354\u1355\u1356\u1357\u1358\u1359\u135a\u1380\u1381\u1382\u1383\u1384\u1385\u1386\u1387\u1388\u1389\u138a\u138b\u138c\u138d\u138e\u138f\u13a0\u13a1\u13a2\u13a3\u13a4\u13a5\u13a6\u13a7\u13a8\u13a9\u13aa\u13ab\u13ac\u13ad\u13ae\u13af\u13b0\u13b1\u13b2\u13b3\u13b4\u13b5\u13b6\u13b7\u13b8\u13b9\u13ba\u13bb\u13bc\u13bd\u13be\u13bf\u13c0\u13c1\u13c2\u13c3\u13c4\u13c5\u13c6\u13c7\u13c8\u13c9\u13ca\u13cb\u13cc\u13cd\u13ce\u13cf\u13d0\u13d1\u13d2\u13d3\u13d4\u13d5\u13d6\u13d7\u13d8\u13d9\u13da\u13db\u13dc\u13dd\u13de\u13df\u13e0\u13e1\u13e2\u13e3\u13e4\u13e5\u13e6\u13e7\u13e8\u13e9\u13ea\u13eb\u13ec\u13ed\u13ee\u13ef\u13f0\u13f1\u13f2\u13f3\u13f4\u1401\u1402\u1403\u1404\u1405\u1406\u1407\u1408\u1409\u140a\u140b\u140c\u140d\u140e\u140f\u1410\u1411\u1412\u1413\u1414\u1415\u1416\u1417\u1418\u1419\u141a\u141b\u141c\u141d\u141e\u141f\u1420\u1421\u1422\u1423\u1424\u1425\u1426\u1427\u1428\u1429\u142a\u142b\u142c\u142d\u142e\u142f\u1430\u1431\u1432\u1433\u1434\u1435\u1436\u1437\u1438\u1439\u143a\u143b\u143c\u143d\u143e\u143f\u1440\u1441\u1442\u1443\u1444\u1445\u1446\u1447\u1448\u1449\u144a\u144b\u144c\u144d\u144e\u144f\u1450\u1451\u1452\u1453\u1454\u1455\u1456\u1457\u1458\u1459\u145a\u145b\u145c\u145d\u145e\u145f\u1460\u1461\u1462\u1463\u1464\u1465\u1466\u1467\u1468\u1469\u146a\u146b\u146c\u146d\u146e\u146f\u1470\u1471\u1472\u1473\u1474\u1475\u1476\u1477\u1478\u1479\u147a\u147b\u147c\u147d\u147e\u147f\u1480\u1481\u1482\u1483\u1484\u1485\u1486\u1487\u1488\u1489\u148a\u148b\u148c\u148d\u148e\u148f\u1490\u1491\u1492\u1493\u1494\u1495\u1496\u1497\u1498\u1499\u149a\u149b\u149c\u149d\u149e\u149f\u14a0\u14a1\u14a2\u14a3\u14a4\u14a5\u14a6\u14a7\u14a8\u14a9\u14aa\u14ab\u14ac\u14ad\u14ae\u14af\u14b0\u14b1\u14b2\u14b3\u14b4\u14b5\u14b6\u14b7\u14b8\u14b9\u14ba\u14bb\u14bc\u14bd\u14be\u14bf\u14c0\u14c1\u14c2\u14c3\u14c4\u14c5\u14c6\u14c7\u14c8\u14c9\u14ca\u14cb\u14cc\u14cd\u14ce\u14cf\u14d0\u14d1\u14d2\u14d3\u14d4\u14d5\u14d6\u14d7\u14d8\u14d9\u14da\u14db\u14dc\u14dd\u14de\u14df\u14e0\u14e1\u14e2\u14e3\u14e4\u14e5\u14e6\u14e7\u14e8\u14e9\u14ea\u14eb\u14ec\u14ed\u14ee\u14ef\u14f0\u14f1\u14f2\u14f3\u14f4\u14f5\u14f6\u14f7\u14f8\u14f9\u14fa\u14fb\u14fc\u14fd\u14fe\u14ff\u1500\u1501\u1502\u1503\u1504\u1505\u1506\u1507\u1508\u1509\u150a\u150b\u150c\u150d\u150e\u150f\u1510\u1511\u1512\u1513\u1514\u1515\u1516\u1517\u1518\u1519\u151a\u151b\u151c\u151d\u151e\u151f\u1520\u1521\u1522\u1523\u1524\u1525\u1526\u1527\u1528\u1529\u152a\u152b\u152c\u152d\u152e\u152f\u1530\u1531\u1532\u1533\u1534\u1535\u1536\u1537\u1538\u1539\u153a\u153b\u153c\u153d\u153e\u153f\u1540\u1541\u1542\u1543\u1544\u1545\u1546\u1547\u1548\u1549\u154a\u154b\u154c\u154d\u154e\u154f\u1550\u1551\u1552\u1553\u1554\u1555\u1556\u1557\u1558\u1559\u155a\u155b\u155c\u155d\u155e\u155f\u1560\u1561\u1562\u1563\u1564\u1565\u1566\u1567\u1568\u1569\u156a\u156b\u156c\u156d\u156e\u156f\u1570\u1571\u1572\u1573\u1574\u1575\u1576\u1577\u1578\u1579\u157a\u157b\u157c\u157d\u157e\u157f\u1580\u1581\u1582\u1583\u1584\u1585\u1586\u1587\u1588\u1589\u158a\u158b\u158c\u158d\u158e\u158f\u1590\u1591\u1592\u1593\u1594\u1595\u1596\u1597\u1598\u1599\u159a\u159b\u159c\u159d\u159e\u159f\u15a0\u15a1\u15a2\u15a3\u15a4\u15a5\u15a6\u15a7\u15a8\u15a9\u15aa\u15ab\u15ac\u15ad\u15ae\u15af\u15b0\u15b1\u15b2\u15b3\u15b4\u15b5\u15b6\u15b7\u15b8\u15b9\u15ba\u15bb\u15bc\u15bd\u15be\u15bf\u15c0\u15c1\u15c2\u15c3\u15c4\u15c5\u15c6\u15c7\u15c8\u15c9\u15ca\u15cb\u15cc\u15cd\u15ce\u15cf\u15d0\u15d1\u15d2\u15d3\u15d4\u15d5\u15d6\u15d7\u15d8\u15d9\u15da\u15db\u15dc\u15dd\u15de\u15df\u15e0\u15e1\u15e2\u15e3\u15e4\u15e5\u15e6\u15e7\u15e8\u15e9\u15ea\u15eb\u15ec\u15ed\u15ee\u15ef\u15f0\u15f1\u15f2\u15f3\u15f4\u15f5\u15f6\u15f7\u15f8\u15f9\u15fa\u15fb\u15fc\u15fd\u15fe\u15ff\u1600\u1601\u1602\u1603\u1604\u1605\u1606\u1607\u1608\u1609\u160a\u160b\u160c\u160d\u160e\u160f\u1610\u1611\u1612\u1613\u1614\u1615\u1616\u1617\u1618\u1619\u161a\u161b\u161c\u161d\u161e\u161f\u1620\u1621\u1622\u1623\u1624\u1625\u1626\u1627\u1628\u1629\u162a\u162b\u162c\u162d\u162e\u162f\u1630\u1631\u1632\u1633\u1634\u1635\u1636\u1637\u1638\u1639\u163a\u163b\u163c\u163d\u163e\u163f\u1640\u1641\u1642\u1643\u1644\u1645\u1646\u1647\u1648\u1649\u164a\u164b\u164c\u164d\u164e\u164f\u1650\u1651\u1652\u1653\u1654\u1655\u1656\u1657\u1658\u1659\u165a\u165b\u165c\u165d\u165e\u165f\u1660\u1661\u1662\u1663\u1664\u1665\u1666\u1667\u1668\u1669\u166a\u166b\u166c\u166f\u1670\u1671\u1672\u1673\u1674\u1675\u1676\u1681\u1682\u1683\u1684\u1685\u1686\u1687\u1688\u1689\u168a\u168b\u168c\u168d\u168e\u168f\u1690\u1691\u1692\u1693\u1694\u1695\u1696\u1697\u1698\u1699\u169a\u16a0\u16a1\u16a2\u16a3\u16a4\u16a5\u16a6\u16a7\u16a8\u16a9\u16aa\u16ab\u16ac\u16ad\u16ae\u16af\u16b0\u16b1\u16b2\u16b3\u16b4\u16b5\u16b6\u16b7\u16b8\u16b9\u16ba\u16bb\u16bc\u16bd\u16be\u16bf\u16c0\u16c1\u16c2\u16c3\u16c4\u16c5\u16c6\u16c7\u16c8\u16c9\u16ca\u16cb\u16cc\u16cd\u16ce\u16cf\u16d0\u16d1\u16d2\u16d3\u16d4\u16d5\u16d6\u16d7\u16d8\u16d9\u16da\u16db\u16dc\u16dd\u16de\u16df\u16e0\u16e1\u16e2\u16e3\u16e4\u16e5\u16e6\u16e7\u16e8\u16e9\u16ea\u1700\u1701\u1702\u1703\u1704\u1705\u1706\u1707\u1708\u1709\u170a\u170b\u170c\u170e\u170f\u1710\u1711\u1720\u1721\u1722\u1723\u1724\u1725\u1726\u1727\u1728\u1729\u172a\u172b\u172c\u172d\u172e\u172f\u1730\u1731\u1740\u1741\u1742\u1743\u1744\u1745\u1746\u1747\u1748\u1749\u174a\u174b\u174c\u174d\u174e\u174f\u1750\u1751\u1760\u1761\u1762\u1763\u1764\u1765\u1766\u1767\u1768\u1769\u176a\u176b\u176c\u176e\u176f\u1770\u1780\u1781\u1782\u1783\u1784\u1785\u1786\u1787\u1788\u1789\u178a\u178b\u178c\u178d\u178e\u178f\u1790\u1791\u1792\u1793\u1794\u1795\u1796\u1797\u1798\u1799\u179a\u179b\u179c\u179d\u179e\u179f\u17a0\u17a1\u17a2\u17a3\u17a4\u17a5\u17a6\u17a7\u17a8\u17a9\u17aa\u17ab\u17ac\u17ad\u17ae\u17af\u17b0\u17b1\u17b2\u17b3\u17dc\u1820\u1821\u1822\u1823\u1824\u1825\u1826\u1827\u1828\u1829\u182a\u182b\u182c\u182d\u182e\u182f\u1830\u1831\u1832\u1833\u1834\u1835\u1836\u1837\u1838\u1839\u183a\u183b\u183c\u183d\u183e\u183f\u1840\u1841\u1842\u1844\u1845\u1846\u1847\u1848\u1849\u184a\u184b\u184c\u184d\u184e\u184f\u1850\u1851\u1852\u1853\u1854\u1855\u1856\u1857\u1858\u1859\u185a\u185b\u185c\u185d\u185e\u185f\u1860\u1861\u1862\u1863\u1864\u1865\u1866\u1867\u1868\u1869\u186a\u186b\u186c\u186d\u186e\u186f\u1870\u1871\u1872\u1873\u1874\u1875\u1876\u1877\u1880\u1881\u1882\u1883\u1884\u1885\u1886\u1887\u1888\u1889\u188a\u188b\u188c\u188d\u188e\u188f\u1890\u1891\u1892\u1893\u1894\u1895\u1896\u1897\u1898\u1899\u189a\u189b\u189c\u189d\u189e\u189f\u18a0\u18a1\u18a2\u18a3\u18a4\u18a5\u18a6\u18a7\u18a8\u1900\u1901\u1902\u1903\u1904\u1905\u1906\u1907\u1908\u1909\u190a\u190b\u190c\u190d\u190e\u190f\u1910\u1911\u1912\u1913\u1914\u1915\u1916\u1917\u1918\u1919\u191a\u191b\u191c\u1950\u1951\u1952\u1953\u1954\u1955\u1956\u1957\u1958\u1959\u195a\u195b\u195c\u195d\u195e\u195f\u1960\u1961\u1962\u1963\u1964\u1965\u1966\u1967\u1968\u1969\u196a\u196b\u196c\u196d\u1970\u1971\u1972\u1973\u1974\u1980\u1981\u1982\u1983\u1984\u1985\u1986\u1987\u1988\u1989\u198a\u198b\u198c\u198d\u198e\u198f\u1990\u1991\u1992\u1993\u1994\u1995\u1996\u1997\u1998\u1999\u199a\u199b\u199c\u199d\u199e\u199f\u19a0\u19a1\u19a2\u19a3\u19a4\u19a5\u19a6\u19a7\u19a8\u19a9\u19c1\u19c2\u19c3\u19c4\u19c5\u19c6\u19c7\u1a00\u1a01\u1a02\u1a03\u1a04\u1a05\u1a06\u1a07\u1a08\u1a09\u1a0a\u1a0b\u1a0c\u1a0d\u1a0e\u1a0f\u1a10\u1a11\u1a12\u1a13\u1a14\u1a15\u1a16\u2135\u2136\u2137\u2138\u2d30\u2d31\u2d32\u2d33\u2d34\u2d35\u2d36\u2d37\u2d38\u2d39\u2d3a\u2d3b\u2d3c\u2d3d\u2d3e\u2d3f\u2d40\u2d41\u2d42\u2d43\u2d44\u2d45\u2d46\u2d47\u2d48\u2d49\u2d4a\u2d4b\u2d4c\u2d4d\u2d4e\u2d4f\u2d50\u2d51\u2d52\u2d53\u2d54\u2d55\u2d56\u2d57\u2d58\u2d59\u2d5a\u2d5b\u2d5c\u2d5d\u2d5e\u2d5f\u2d60\u2d61\u2d62\u2d63\u2d64\u2d65\u2d80\u2d81\u2d82\u2d83\u2d84\u2d85\u2d86\u2d87\u2d88\u2d89\u2d8a\u2d8b\u2d8c\u2d8d\u2d8e\u2d8f\u2d90\u2d91\u2d92\u2d93\u2d94\u2d95\u2d96\u2da0\u2da1\u2da2\u2da3\u2da4\u2da5\u2da6\u2da8\u2da9\u2daa\u2dab\u2dac\u2dad\u2dae\u2db0\u2db1\u2db2\u2db3\u2db4\u2db5\u2db6\u2db8\u2db9\u2dba\u2dbb\u2dbc\u2dbd\u2dbe\u2dc0\u2dc1\u2dc2\u2dc3\u2dc4\u2dc5\u2dc6\u2dc8\u2dc9\u2dca\u2dcb\u2dcc\u2dcd\u2dce\u2dd0\u2dd1\u2dd2\u2dd3\u2dd4\u2dd5\u2dd6\u2dd8\u2dd9\u2dda\u2ddb\u2ddc\u2ddd\u2dde\u3006\u303c\u3041\u3042\u3043\u3044\u3045\u3046\u3047\u3048\u3049\u304a\u304b\u304c\u304d\u304e\u304f\u3050\u3051\u3052\u3053\u3054\u3055\u3056\u3057\u3058\u3059\u305a\u305b\u305c\u305d\u305e\u305f\u3060\u3061\u3062\u3063\u3064\u3065\u3066\u3067\u3068\u3069\u306a\u306b\u306c\u306d\u306e\u306f\u3070\u3071\u3072\u3073\u3074\u3075\u3076\u3077\u3078\u3079\u307a\u307b\u307c\u307d\u307e\u307f\u3080\u3081\u3082\u3083\u3084\u3085\u3086\u3087\u3088\u3089\u308a\u308b\u308c\u308d\u308e\u308f\u3090\u3091\u3092\u3093\u3094\u3095\u3096\u309f\u30a1\u30a2\u30a3\u30a4\u30a5\u30a6\u30a7\u30a8\u30a9\u30aa\u30ab\u30ac\u30ad\u30ae\u30af\u30b0\u30b1\u30b2\u30b3\u30b4\u30b5\u30b6\u30b7\u30b8\u30b9\u30ba\u30bb\u30bc\u30bd\u30be\u30bf\u30c0\u30c1\u30c2\u30c3\u30c4\u30c5\u30c6\u30c7\u30c8\u30c9\u30ca\u30cb\u30cc\u30cd\u30ce\u30cf\u30d0\u30d1\u30d2\u30d3\u30d4\u30d5\u30d6\u30d7\u30d8\u30d9\u30da\u30db\u30dc\u30dd\u30de\u30df\u30e0\u30e1\u30e2\u30e3\u30e4\u30e5\u30e6\u30e7\u30e8\u30e9\u30ea\u30eb\u30ec\u30ed\u30ee\u30ef\u30f0\u30f1\u30f2\u30f3\u30f4\u30f5\u30f6\u30f7\u30f8\u30f9\u30fa\u30ff\u3105\u3106\u3107\u3108\u3109\u310a\u310b\u310c\u310d\u310e\u310f\u3110\u3111\u3112\u3113\u3114\u3115\u3116\u3117\u3118\u3119\u311a\u311b\u311c\u311d\u311e\u311f\u3120\u3121\u3122\u3123\u3124\u3125\u3126\u3127\u3128\u3129\u312a\u312b\u312c\u3131\u3132\u3133\u3134\u3135\u3136\u3137\u3138\u3139\u313a\u313b\u313c\u313d\u313e\u313f\u3140\u3141\u3142\u3143\u3144\u3145\u3146\u3147\u3148\u3149\u314a\u314b\u314c\u314d\u314e\u314f\u3150\u3151\u3152\u3153\u3154\u3155\u3156\u3157\u3158\u3159\u315a\u315b\u315c\u315d\u315e\u315f\u3160\u3161\u3162\u3163\u3164\u3165\u3166\u3167\u3168\u3169\u316a\u316b\u316c\u316d\u316e\u316f\u3170\u3171\u3172\u3173\u3174\u3175\u3176\u3177\u3178\u3179\u317a\u317b\u317c\u317d\u317e\u317f\u3180\u3181\u3182\u3183\u3184\u3185\u3186\u3187\u3188\u3189\u318a\u318b\u318c\u318d\u318e\u31a0\u31a1\u31a2\u31a3\u31a4\u31a5\u31a6\u31a7\u31a8\u31a9\u31aa\u31ab\u31ac\u31ad\u31ae\u31af\u31b0\u31b1\u31b2\u31b3\u31b4\u31b5\u31b6\u31b7\u31f0\u31f1\u31f2\u31f3\u31f4\u31f5\u31f6\u31f7\u31f8\u31f9\u31fa\u31fb\u31fc\u31fd\u31fe\u31ff\u3400\u3401\u3402\u3403\u3404\u3405\u3406\u3407\u3408\u3409\u340a\u340b\u340c\u340d\u340e\u340f\u3410\u3411\u3412\u3413\u3414\u3415\u3416\u3417\u3418\u3419\u341a\u341b\u341c\u341d\u341e\u341f\u3420\u3421\u3422\u3423\u3424\u3425\u3426\u3427\u3428\u3429\u342a\u342b\u342c\u342d\u342e\u342f\u3430\u3431\u3432\u3433\u3434\u3435\u3436\u3437\u3438\u3439\u343a\u343b\u343c\u343d\u343e\u343f\u3440\u3441\u3442\u3443\u3444\u3445\u3446\u3447\u3448\u3449\u344a\u344b\u344c\u344d\u344e\u344f\u3450\u3451\u3452\u3453\u3454\u3455\u3456\u3457\u3458\u3459\u345a\u345b\u345c\u345d\u345e\u345f\u3460\u3461\u3462\u3463\u3464\u3465\u3466\u3467\u3468\u3469\u346a\u346b\u346c\u346d\u346e\u346f\u3470\u3471\u3472\u3473\u3474\u3475\u3476\u3477\u3478\u3479\u347a\u347b\u347c\u347d\u347e\u347f\u3480\u3481\u3482\u3483\u3484\u3485\u3486\u3487\u3488\u3489\u348a\u348b\u348c\u348d\u348e\u348f\u3490\u3491\u3492\u3493\u3494\u3495\u3496\u3497\u3498\u3499\u349a\u349b\u349c\u349d\u349e\u349f\u34a0\u34a1\u34a2\u34a3\u34a4\u34a5\u34a6\u34a7\u34a8\u34a9\u34aa\u34ab\u34ac\u34ad\u34ae\u34af\u34b0\u34b1\u34b2\u34b3\u34b4\u34b5\u34b6\u34b7\u34b8\u34b9\u34ba\u34bb\u34bc\u34bd\u34be\u34bf\u34c0\u34c1\u34c2\u34c3\u34c4\u34c5\u34c6\u34c7\u34c8\u34c9\u34ca\u34cb\u34cc\u34cd\u34ce\u34cf\u34d0\u34d1\u34d2\u34d3\u34d4\u34d5\u34d6\u34d7\u34d8\u34d9\u34da\u34db\u34dc\u34dd\u34de\u34df\u34e0\u34e1\u34e2\u34e3\u34e4\u34e5\u34e6\u34e7\u34e8\u34e9\u34ea\u34eb\u34ec\u34ed\u34ee\u34ef\u34f0\u34f1\u34f2\u34f3\u34f4\u34f5\u34f6\u34f7\u34f8\u34f9\u34fa\u34fb\u34fc\u34fd\u34fe\u34ff\u3500\u3501\u3502\u3503\u3504\u3505\u3506\u3507\u3508\u3509\u350a\u350b\u350c\u350d\u350e\u350f\u3510\u3511\u3512\u3513\u3514\u3515\u3516\u3517\u3518\u3519\u351a\u351b\u351c\u351d\u351e\u351f\u3520\u3521\u3522\u3523\u3524\u3525\u3526\u3527\u3528\u3529\u352a\u352b\u352c\u352d\u352e\u352f\u3530\u3531\u3532\u3533\u3534\u3535\u3536\u3537\u3538\u3539\u353a\u353b\u353c\u353d\u353e\u353f\u3540\u3541\u3542\u3543\u3544\u3545\u3546\u3547\u3548\u3549\u354a\u354b\u354c\u354d\u354e\u354f\u3550\u3551\u3552\u3553\u3554\u3555\u3556\u3557\u3558\u3559\u355a\u355b\u355c\u355d\u355e\u355f\u3560\u3561\u3562\u3563\u3564\u3565\u3566\u3567\u3568\u3569\u356a\u356b\u356c\u356d\u356e\u356f\u3570\u3571\u3572\u3573\u3574\u3575\u3576\u3577\u3578\u3579\u357a\u357b\u357c\u357d\u357e\u357f\u3580\u3581\u3582\u3583\u3584\u3585\u3586\u3587\u3588\u3589\u358a\u358b\u358c\u358d\u358e\u358f\u3590\u3591\u3592\u3593\u3594\u3595\u3596\u3597\u3598\u3599\u359a\u359b\u359c\u359d\u359e\u359f\u35a0\u35a1\u35a2\u35a3\u35a4\u35a5\u35a6\u35a7\u35a8\u35a9\u35aa\u35ab\u35ac\u35ad\u35ae\u35af\u35b0\u35b1\u35b2\u35b3\u35b4\u35b5\u35b6\u35b7\u35b8\u35b9\u35ba\u35bb\u35bc\u35bd\u35be\u35bf\u35c0\u35c1\u35c2\u35c3\u35c4\u35c5\u35c6\u35c7\u35c8\u35c9\u35ca\u35cb\u35cc\u35cd\u35ce\u35cf\u35d0\u35d1\u35d2\u35d3\u35d4\u35d5\u35d6\u35d7\u35d8\u35d9\u35da\u35db\u35dc\u35dd\u35de\u35df\u35e0\u35e1\u35e2\u35e3\u35e4\u35e5\u35e6\u35e7\u35e8\u35e9\u35ea\u35eb\u35ec\u35ed\u35ee\u35ef\u35f0\u35f1\u35f2\u35f3\u35f4\u35f5\u35f6\u35f7\u35f8\u35f9\u35fa\u35fb\u35fc\u35fd\u35fe\u35ff\u3600\u3601\u3602\u3603\u3604\u3605\u3606\u3607\u3608\u3609\u360a\u360b\u360c\u360d\u360e\u360f\u3610\u3611\u3612\u3613\u3614\u3615\u3616\u3617\u3618\u3619\u361a\u361b\u361c\u361d\u361e\u361f\u3620\u3621\u3622\u3623\u3624\u3625\u3626\u3627\u3628\u3629\u362a\u362b\u362c\u362d\u362e\u362f\u3630\u3631\u3632\u3633\u3634\u3635\u3636\u3637\u3638\u3639\u363a\u363b\u363c\u363d\u363e\u363f\u3640\u3641\u3642\u3643\u3644\u3645\u3646\u3647\u3648\u3649\u364a\u364b\u364c\u364d\u364e\u364f\u3650\u3651\u3652\u3653\u3654\u3655\u3656\u3657\u3658\u3659\u365a\u365b\u365c\u365d\u365e\u365f\u3660\u3661\u3662\u3663\u3664\u3665\u3666\u3667\u3668\u3669\u366a\u366b\u366c\u366d\u366e\u366f\u3670\u3671\u3672\u3673\u3674\u3675\u3676\u3677\u3678\u3679\u367a\u367b\u367c\u367d\u367e\u367f\u3680\u3681\u3682\u3683\u3684\u3685\u3686\u3687\u3688\u3689\u368a\u368b\u368c\u368d\u368e\u368f\u3690\u3691\u3692\u3693\u3694\u3695\u3696\u3697\u3698\u3699\u369a\u369b\u369c\u369d\u369e\u369f\u36a0\u36a1\u36a2\u36a3\u36a4\u36a5\u36a6\u36a7\u36a8\u36a9\u36aa\u36ab\u36ac\u36ad\u36ae\u36af\u36b0\u36b1\u36b2\u36b3\u36b4\u36b5\u36b6\u36b7\u36b8\u36b9\u36ba\u36bb\u36bc\u36bd\u36be\u36bf\u36c0\u36c1\u36c2\u36c3\u36c4\u36c5\u36c6\u36c7\u36c8\u36c9\u36ca\u36cb\u36cc\u36cd\u36ce\u36cf\u36d0\u36d1\u36d2\u36d3\u36d4\u36d5\u36d6\u36d7\u36d8\u36d9\u36da\u36db\u36dc\u36dd\u36de\u36df\u36e0\u36e1\u36e2\u36e3\u36e4\u36e5\u36e6\u36e7\u36e8\u36e9\u36ea\u36eb\u36ec\u36ed\u36ee\u36ef\u36f0\u36f1\u36f2\u36f3\u36f4\u36f5\u36f6\u36f7\u36f8\u36f9\u36fa\u36fb\u36fc\u36fd\u36fe\u36ff\u3700\u3701\u3702\u3703\u3704\u3705\u3706\u3707\u3708\u3709\u370a\u370b\u370c\u370d\u370e\u370f\u3710\u3711\u3712\u3713\u3714\u3715\u3716\u3717\u3718\u3719\u371a\u371b\u371c\u371d\u371e\u371f\u3720\u3721\u3722\u3723\u3724\u3725\u3726\u3727\u3728\u3729\u372a\u372b\u372c\u372d\u372e\u372f\u3730\u3731\u3732\u3733\u3734\u3735\u3736\u3737\u3738\u3739\u373a\u373b\u373c\u373d\u373e\u373f\u3740\u3741\u3742\u3743\u3744\u3745\u3746\u3747\u3748\u3749\u374a\u374b\u374c\u374d\u374e\u374f\u3750\u3751\u3752\u3753\u3754\u3755\u3756\u3757\u3758\u3759\u375a\u375b\u375c\u375d\u375e\u375f\u3760\u3761\u3762\u3763\u3764\u3765\u3766\u3767\u3768\u3769\u376a\u376b\u376c\u376d\u376e\u376f\u3770\u3771\u3772\u3773\u3774\u3775\u3776\u3777\u3778\u3779\u377a\u377b\u377c\u377d\u377e\u377f\u3780\u3781\u3782\u3783\u3784\u3785\u3786\u3787\u3788\u3789\u378a\u378b\u378c\u378d\u378e\u378f\u3790\u3791\u3792\u3793\u3794\u3795\u3796\u3797\u3798\u3799\u379a\u379b\u379c\u379d\u379e\u379f\u37a0\u37a1\u37a2\u37a3\u37a4\u37a5\u37a6\u37a7\u37a8\u37a9\u37aa\u37ab\u37ac\u37ad\u37ae\u37af\u37b0\u37b1\u37b2\u37b3\u37b4\u37b5\u37b6\u37b7\u37b8\u37b9\u37ba\u37bb\u37bc\u37bd\u37be\u37bf\u37c0\u37c1\u37c2\u37c3\u37c4\u37c5\u37c6\u37c7\u37c8\u37c9\u37ca\u37cb\u37cc\u37cd\u37ce\u37cf\u37d0\u37d1\u37d2\u37d3\u37d4\u37d5\u37d6\u37d7\u37d8\u37d9\u37da\u37db\u37dc\u37dd\u37de\u37df\u37e0\u37e1\u37e2\u37e3\u37e4\u37e5\u37e6\u37e7\u37e8\u37e9\u37ea\u37eb\u37ec\u37ed\u37ee\u37ef\u37f0\u37f1\u37f2\u37f3\u37f4\u37f5\u37f6\u37f7\u37f8\u37f9\u37fa\u37fb\u37fc\u37fd\u37fe\u37ff\u3800\u3801\u3802\u3803\u3804\u3805\u3806\u3807\u3808\u3809\u380a\u380b\u380c\u380d\u380e\u380f\u3810\u3811\u3812\u3813\u3814\u3815\u3816\u3817\u3818\u3819\u381a\u381b\u381c\u381d\u381e\u381f\u3820\u3821\u3822\u3823\u3824\u3825\u3826\u3827\u3828\u3829\u382a\u382b\u382c\u382d\u382e\u382f\u3830\u3831\u3832\u3833\u3834\u3835\u3836\u3837\u3838\u3839\u383a\u383b\u383c\u383d\u383e\u383f\u3840\u3841\u3842\u3843\u3844\u3845\u3846\u3847\u3848\u3849\u384a\u384b\u384c\u384d\u384e\u384f\u3850\u3851\u3852\u3853\u3854\u3855\u3856\u3857\u3858\u3859\u385a\u385b\u385c\u385d\u385e\u385f\u3860\u3861\u3862\u3863\u3864\u3865\u3866\u3867\u3868\u3869\u386a\u386b\u386c\u386d\u386e\u386f\u3870\u3871\u3872\u3873\u3874\u3875\u3876\u3877\u3878\u3879\u387a\u387b\u387c\u387d\u387e\u387f\u3880\u3881\u3882\u3883\u3884\u3885\u3886\u3887\u3888\u3889\u388a\u388b\u388c\u388d\u388e\u388f\u3890\u3891\u3892\u3893\u3894\u3895\u3896\u3897\u3898\u3899\u389a\u389b\u389c\u389d\u389e\u389f\u38a0\u38a1\u38a2\u38a3\u38a4\u38a5\u38a6\u38a7\u38a8\u38a9\u38aa\u38ab\u38ac\u38ad\u38ae\u38af\u38b0\u38b1\u38b2\u38b3\u38b4\u38b5\u38b6\u38b7\u38b8\u38b9\u38ba\u38bb\u38bc\u38bd\u38be\u38bf\u38c0\u38c1\u38c2\u38c3\u38c4\u38c5\u38c6\u38c7\u38c8\u38c9\u38ca\u38cb\u38cc\u38cd\u38ce\u38cf\u38d0\u38d1\u38d2\u38d3\u38d4\u38d5\u38d6\u38d7\u38d8\u38d9\u38da\u38db\u38dc\u38dd\u38de\u38df\u38e0\u38e1\u38e2\u38e3\u38e4\u38e5\u38e6\u38e7\u38e8\u38e9\u38ea\u38eb\u38ec\u38ed\u38ee\u38ef\u38f0\u38f1\u38f2\u38f3\u38f4\u38f5\u38f6\u38f7\u38f8\u38f9\u38fa\u38fb\u38fc\u38fd\u38fe\u38ff\u3900\u3901\u3902\u3903\u3904\u3905\u3906\u3907\u3908\u3909\u390a\u390b\u390c\u390d\u390e\u390f\u3910\u3911\u3912\u3913\u3914\u3915\u3916\u3917\u3918\u3919\u391a\u391b\u391c\u391d\u391e\u391f\u3920\u3921\u3922\u3923\u3924\u3925\u3926\u3927\u3928\u3929\u392a\u392b\u392c\u392d\u392e\u392f\u3930\u3931\u3932\u3933\u3934\u3935\u3936\u3937\u3938\u3939\u393a\u393b\u393c\u393d\u393e\u393f\u3940\u3941\u3942\u3943\u3944\u3945\u3946\u3947\u3948\u3949\u394a\u394b\u394c\u394d\u394e\u394f\u3950\u3951\u3952\u3953\u3954\u3955\u3956\u3957\u3958\u3959\u395a\u395b\u395c\u395d\u395e\u395f\u3960\u3961\u3962\u3963\u3964\u3965\u3966\u3967\u3968\u3969\u396a\u396b\u396c\u396d\u396e\u396f\u3970\u3971\u3972\u3973\u3974\u3975\u3976\u3977\u3978\u3979\u397a\u397b\u397c\u397d\u397e\u397f\u3980\u3981\u3982\u3983\u3984\u3985\u3986\u3987\u3988\u3989\u398a\u398b\u398c\u398d\u398e\u398f\u3990\u3991\u3992\u3993\u3994\u3995\u3996\u3997\u3998\u3999\u399a\u399b\u399c\u399d\u399e\u399f\u39a0\u39a1\u39a2\u39a3\u39a4\u39a5\u39a6\u39a7\u39a8\u39a9\u39aa\u39ab\u39ac\u39ad\u39ae\u39af\u39b0\u39b1\u39b2\u39b3\u39b4\u39b5\u39b6\u39b7\u39b8\u39b9\u39ba\u39bb\u39bc\u39bd\u39be\u39bf\u39c0\u39c1\u39c2\u39c3\u39c4\u39c5\u39c6\u39c7\u39c8\u39c9\u39ca\u39cb\u39cc\u39cd\u39ce\u39cf\u39d0\u39d1\u39d2\u39d3\u39d4\u39d5\u39d6\u39d7\u39d8\u39d9\u39da\u39db\u39dc\u39dd\u39de\u39df\u39e0\u39e1\u39e2\u39e3\u39e4\u39e5\u39e6\u39e7\u39e8\u39e9\u39ea\u39eb\u39ec\u39ed\u39ee\u39ef\u39f0\u39f1\u39f2\u39f3\u39f4\u39f5\u39f6\u39f7\u39f8\u39f9\u39fa\u39fb\u39fc\u39fd\u39fe\u39ff\u3a00\u3a01\u3a02\u3a03\u3a04\u3a05\u3a06\u3a07\u3a08\u3a09\u3a0a\u3a0b\u3a0c\u3a0d\u3a0e\u3a0f\u3a10\u3a11\u3a12\u3a13\u3a14\u3a15\u3a16\u3a17\u3a18\u3a19\u3a1a\u3a1b\u3a1c\u3a1d\u3a1e\u3a1f\u3a20\u3a21\u3a22\u3a23\u3a24\u3a25\u3a26\u3a27\u3a28\u3a29\u3a2a\u3a2b\u3a2c\u3a2d\u3a2e\u3a2f\u3a30\u3a31\u3a32\u3a33\u3a34\u3a35\u3a36\u3a37\u3a38\u3a39\u3a3a\u3a3b\u3a3c\u3a3d\u3a3e\u3a3f\u3a40\u3a41\u3a42\u3a43\u3a44\u3a45\u3a46\u3a47\u3a48\u3a49\u3a4a\u3a4b\u3a4c\u3a4d\u3a4e\u3a4f\u3a50\u3a51\u3a52\u3a53\u3a54\u3a55\u3a56\u3a57\u3a58\u3a59\u3a5a\u3a5b\u3a5c\u3a5d\u3a5e\u3a5f\u3a60\u3a61\u3a62\u3a63\u3a64\u3a65\u3a66\u3a67\u3a68\u3a69\u3a6a\u3a6b\u3a6c\u3a6d\u3a6e\u3a6f\u3a70\u3a71\u3a72\u3a73\u3a74\u3a75\u3a76\u3a77\u3a78\u3a79\u3a7a\u3a7b\u3a7c\u3a7d\u3a7e\u3a7f\u3a80\u3a81\u3a82\u3a83\u3a84\u3a85\u3a86\u3a87\u3a88\u3a89\u3a8a\u3a8b\u3a8c\u3a8d\u3a8e\u3a8f\u3a90\u3a91\u3a92\u3a93\u3a94\u3a95\u3a96\u3a97\u3a98\u3a99\u3a9a\u3a9b\u3a9c\u3a9d\u3a9e\u3a9f\u3aa0\u3aa1\u3aa2\u3aa3\u3aa4\u3aa5\u3aa6\u3aa7\u3aa8\u3aa9\u3aaa\u3aab\u3aac\u3aad\u3aae\u3aaf\u3ab0\u3ab1\u3ab2\u3ab3\u3ab4\u3ab5\u3ab6\u3ab7\u3ab8\u3ab9\u3aba\u3abb\u3abc\u3abd\u3abe\u3abf\u3ac0\u3ac1\u3ac2\u3ac3\u3ac4\u3ac5\u3ac6\u3ac7\u3ac8\u3ac9\u3aca\u3acb\u3acc\u3acd\u3ace\u3acf\u3ad0\u3ad1\u3ad2\u3ad3\u3ad4\u3ad5\u3ad6\u3ad7\u3ad8\u3ad9\u3ada\u3adb\u3adc\u3add\u3ade\u3adf\u3ae0\u3ae1\u3ae2\u3ae3\u3ae4\u3ae5\u3ae6\u3ae7\u3ae8\u3ae9\u3aea\u3aeb\u3aec\u3aed\u3aee\u3aef\u3af0\u3af1\u3af2\u3af3\u3af4\u3af5\u3af6\u3af7\u3af8\u3af9\u3afa\u3afb\u3afc\u3afd\u3afe\u3aff\u3b00\u3b01\u3b02\u3b03\u3b04\u3b05\u3b06\u3b07\u3b08\u3b09\u3b0a\u3b0b\u3b0c\u3b0d\u3b0e\u3b0f\u3b10\u3b11\u3b12\u3b13\u3b14\u3b15\u3b16\u3b17\u3b18\u3b19\u3b1a\u3b1b\u3b1c\u3b1d\u3b1e\u3b1f\u3b20\u3b21\u3b22\u3b23\u3b24\u3b25\u3b26\u3b27\u3b28\u3b29\u3b2a\u3b2b\u3b2c\u3b2d\u3b2e\u3b2f\u3b30\u3b31\u3b32\u3b33\u3b34\u3b35\u3b36\u3b37\u3b38\u3b39\u3b3a\u3b3b\u3b3c\u3b3d\u3b3e\u3b3f\u3b40\u3b41\u3b42\u3b43\u3b44\u3b45\u3b46\u3b47\u3b48\u3b49\u3b4a\u3b4b\u3b4c\u3b4d\u3b4e\u3b4f\u3b50\u3b51\u3b52\u3b53\u3b54\u3b55\u3b56\u3b57\u3b58\u3b59\u3b5a\u3b5b\u3b5c\u3b5d\u3b5e\u3b5f\u3b60\u3b61\u3b62\u3b63\u3b64\u3b65\u3b66\u3b67\u3b68\u3b69\u3b6a\u3b6b\u3b6c\u3b6d\u3b6e\u3b6f\u3b70\u3b71\u3b72\u3b73\u3b74\u3b75\u3b76\u3b77\u3b78\u3b79\u3b7a\u3b7b\u3b7c\u3b7d\u3b7e\u3b7f\u3b80\u3b81\u3b82\u3b83\u3b84\u3b85\u3b86\u3b87\u3b88\u3b89\u3b8a\u3b8b\u3b8c\u3b8d\u3b8e\u3b8f\u3b90\u3b91\u3b92\u3b93\u3b94\u3b95\u3b96\u3b97\u3b98\u3b99\u3b9a\u3b9b\u3b9c\u3b9d\u3b9e\u3b9f\u3ba0\u3ba1\u3ba2\u3ba3\u3ba4\u3ba5\u3ba6\u3ba7\u3ba8\u3ba9\u3baa\u3bab\u3bac\u3bad\u3bae\u3baf\u3bb0\u3bb1\u3bb2\u3bb3\u3bb4\u3bb5\u3bb6\u3bb7\u3bb8\u3bb9\u3bba\u3bbb\u3bbc\u3bbd\u3bbe\u3bbf\u3bc0\u3bc1\u3bc2\u3bc3\u3bc4\u3bc5\u3bc6\u3bc7\u3bc8\u3bc9\u3bca\u3bcb\u3bcc\u3bcd\u3bce\u3bcf\u3bd0\u3bd1\u3bd2\u3bd3\u3bd4\u3bd5\u3bd6\u3bd7\u3bd8\u3bd9\u3bda\u3bdb\u3bdc\u3bdd\u3bde\u3bdf\u3be0\u3be1\u3be2\u3be3\u3be4\u3be5\u3be6\u3be7\u3be8\u3be9\u3bea\u3beb\u3bec\u3bed\u3bee\u3bef\u3bf0\u3bf1\u3bf2\u3bf3\u3bf4\u3bf5\u3bf6\u3bf7\u3bf8\u3bf9\u3bfa\u3bfb\u3bfc\u3bfd\u3bfe\u3bff\u3c00\u3c01\u3c02\u3c03\u3c04\u3c05\u3c06\u3c07\u3c08\u3c09\u3c0a\u3c0b\u3c0c\u3c0d\u3c0e\u3c0f\u3c10\u3c11\u3c12\u3c13\u3c14\u3c15\u3c16\u3c17\u3c18\u3c19\u3c1a\u3c1b\u3c1c\u3c1d\u3c1e\u3c1f\u3c20\u3c21\u3c22\u3c23\u3c24\u3c25\u3c26\u3c27\u3c28\u3c29\u3c2a\u3c2b\u3c2c\u3c2d\u3c2e\u3c2f\u3c30\u3c31\u3c32\u3c33\u3c34\u3c35\u3c36\u3c37\u3c38\u3c39\u3c3a\u3c3b\u3c3c\u3c3d\u3c3e\u3c3f\u3c40\u3c41\u3c42\u3c43\u3c44\u3c45\u3c46\u3c47\u3c48\u3c49\u3c4a\u3c4b\u3c4c\u3c4d\u3c4e\u3c4f\u3c50\u3c51\u3c52\u3c53\u3c54\u3c55\u3c56\u3c57\u3c58\u3c59\u3c5a\u3c5b\u3c5c\u3c5d\u3c5e\u3c5f\u3c60\u3c61\u3c62\u3c63\u3c64\u3c65\u3c66\u3c67\u3c68\u3c69\u3c6a\u3c6b\u3c6c\u3c6d\u3c6e\u3c6f\u3c70\u3c71\u3c72\u3c73\u3c74\u3c75\u3c76\u3c77\u3c78\u3c79\u3c7a\u3c7b\u3c7c\u3c7d\u3c7e\u3c7f\u3c80\u3c81\u3c82\u3c83\u3c84\u3c85\u3c86\u3c87\u3c88\u3c89\u3c8a\u3c8b\u3c8c\u3c8d\u3c8e\u3c8f\u3c90\u3c91\u3c92\u3c93\u3c94\u3c95\u3c96\u3c97\u3c98\u3c99\u3c9a\u3c9b\u3c9c\u3c9d\u3c9e\u3c9f\u3ca0\u3ca1\u3ca2\u3ca3\u3ca4\u3ca5\u3ca6\u3ca7\u3ca8\u3ca9\u3caa\u3cab\u3cac\u3cad\u3cae\u3caf\u3cb0\u3cb1\u3cb2\u3cb3\u3cb4\u3cb5\u3cb6\u3cb7\u3cb8\u3cb9\u3cba\u3cbb\u3cbc\u3cbd\u3cbe\u3cbf\u3cc0\u3cc1\u3cc2\u3cc3\u3cc4\u3cc5\u3cc6\u3cc7\u3cc8\u3cc9\u3cca\u3ccb\u3ccc\u3ccd\u3cce\u3ccf\u3cd0\u3cd1\u3cd2\u3cd3\u3cd4\u3cd5\u3cd6\u3cd7\u3cd8\u3cd9\u3cda\u3cdb\u3cdc\u3cdd\u3cde\u3cdf\u3ce0\u3ce1\u3ce2\u3ce3\u3ce4\u3ce5\u3ce6\u3ce7\u3ce8\u3ce9\u3cea\u3ceb\u3cec\u3ced\u3cee\u3cef\u3cf0\u3cf1\u3cf2\u3cf3\u3cf4\u3cf5\u3cf6\u3cf7\u3cf8\u3cf9\u3cfa\u3cfb\u3cfc\u3cfd\u3cfe\u3cff\u3d00\u3d01\u3d02\u3d03\u3d04\u3d05\u3d06\u3d07\u3d08\u3d09\u3d0a\u3d0b\u3d0c\u3d0d\u3d0e\u3d0f\u3d10\u3d11\u3d12\u3d13\u3d14\u3d15\u3d16\u3d17\u3d18\u3d19\u3d1a\u3d1b\u3d1c\u3d1d\u3d1e\u3d1f\u3d20\u3d21\u3d22\u3d23\u3d24\u3d25\u3d26\u3d27\u3d28\u3d29\u3d2a\u3d2b\u3d2c\u3d2d\u3d2e\u3d2f\u3d30\u3d31\u3d32\u3d33\u3d34\u3d35\u3d36\u3d37\u3d38\u3d39\u3d3a\u3d3b\u3d3c\u3d3d\u3d3e\u3d3f\u3d40\u3d41\u3d42\u3d43\u3d44\u3d45\u3d46\u3d47\u3d48\u3d49\u3d4a\u3d4b\u3d4c\u3d4d\u3d4e\u3d4f\u3d50\u3d51\u3d52\u3d53\u3d54\u3d55\u3d56\u3d57\u3d58\u3d59\u3d5a\u3d5b\u3d5c\u3d5d\u3d5e\u3d5f\u3d60\u3d61\u3d62\u3d63\u3d64\u3d65\u3d66\u3d67\u3d68\u3d69\u3d6a\u3d6b\u3d6c\u3d6d\u3d6e\u3d6f\u3d70\u3d71\u3d72\u3d73\u3d74\u3d75\u3d76\u3d77\u3d78\u3d79\u3d7a\u3d7b\u3d7c\u3d7d\u3d7e\u3d7f\u3d80\u3d81\u3d82\u3d83\u3d84\u3d85\u3d86\u3d87\u3d88\u3d89\u3d8a\u3d8b\u3d8c\u3d8d\u3d8e\u3d8f\u3d90\u3d91\u3d92\u3d93\u3d94\u3d95\u3d96\u3d97\u3d98\u3d99\u3d9a\u3d9b\u3d9c\u3d9d\u3d9e\u3d9f\u3da0\u3da1\u3da2\u3da3\u3da4\u3da5\u3da6\u3da7\u3da8\u3da9\u3daa\u3dab\u3dac\u3dad\u3dae\u3daf\u3db0\u3db1\u3db2\u3db3\u3db4\u3db5\u3db6\u3db7\u3db8\u3db9\u3dba\u3dbb\u3dbc\u3dbd\u3dbe\u3dbf\u3dc0\u3dc1\u3dc2\u3dc3\u3dc4\u3dc5\u3dc6\u3dc7\u3dc8\u3dc9\u3dca\u3dcb\u3dcc\u3dcd\u3dce\u3dcf\u3dd0\u3dd1\u3dd2\u3dd3\u3dd4\u3dd5\u3dd6\u3dd7\u3dd8\u3dd9\u3dda\u3ddb\u3ddc\u3ddd\u3dde\u3ddf\u3de0\u3de1\u3de2\u3de3\u3de4\u3de5\u3de6\u3de7\u3de8\u3de9\u3dea\u3deb\u3dec\u3ded\u3dee\u3def\u3df0\u3df1\u3df2\u3df3\u3df4\u3df5\u3df6\u3df7\u3df8\u3df9\u3dfa\u3dfb\u3dfc\u3dfd\u3dfe\u3dff\u3e00\u3e01\u3e02\u3e03\u3e04\u3e05\u3e06\u3e07\u3e08\u3e09\u3e0a\u3e0b\u3e0c\u3e0d\u3e0e\u3e0f\u3e10\u3e11\u3e12\u3e13\u3e14\u3e15\u3e16\u3e17\u3e18\u3e19\u3e1a\u3e1b\u3e1c\u3e1d\u3e1e\u3e1f\u3e20\u3e21\u3e22\u3e23\u3e24\u3e25\u3e26\u3e27\u3e28\u3e29\u3e2a\u3e2b\u3e2c\u3e2d\u3e2e\u3e2f\u3e30\u3e31\u3e32\u3e33\u3e34\u3e35\u3e36\u3e37\u3e38\u3e39\u3e3a\u3e3b\u3e3c\u3e3d\u3e3e\u3e3f\u3e40\u3e41\u3e42\u3e43\u3e44\u3e45\u3e46\u3e47\u3e48\u3e49\u3e4a\u3e4b\u3e4c\u3e4d\u3e4e\u3e4f\u3e50\u3e51\u3e52\u3e53\u3e54\u3e55\u3e56\u3e57\u3e58\u3e59\u3e5a\u3e5b\u3e5c\u3e5d\u3e5e\u3e5f\u3e60\u3e61\u3e62\u3e63\u3e64\u3e65\u3e66\u3e67\u3e68\u3e69\u3e6a\u3e6b\u3e6c\u3e6d\u3e6e\u3e6f\u3e70\u3e71\u3e72\u3e73\u3e74\u3e75\u3e76\u3e77\u3e78\u3e79\u3e7a\u3e7b\u3e7c\u3e7d\u3e7e\u3e7f\u3e80\u3e81\u3e82\u3e83\u3e84\u3e85\u3e86\u3e87\u3e88\u3e89\u3e8a\u3e8b\u3e8c\u3e8d\u3e8e\u3e8f\u3e90\u3e91\u3e92\u3e93\u3e94\u3e95\u3e96\u3e97\u3e98\u3e99\u3e9a\u3e9b\u3e9c\u3e9d\u3e9e\u3e9f\u3ea0\u3ea1\u3ea2\u3ea3\u3ea4\u3ea5\u3ea6\u3ea7\u3ea8\u3ea9\u3eaa\u3eab\u3eac\u3ead\u3eae\u3eaf\u3eb0\u3eb1\u3eb2\u3eb3\u3eb4\u3eb5\u3eb6\u3eb7\u3eb8\u3eb9\u3eba\u3ebb\u3ebc\u3ebd\u3ebe\u3ebf\u3ec0\u3ec1\u3ec2\u3ec3\u3ec4\u3ec5\u3ec6\u3ec7\u3ec8\u3ec9\u3eca\u3ecb\u3ecc\u3ecd\u3ece\u3ecf\u3ed0\u3ed1\u3ed2\u3ed3\u3ed4\u3ed5\u3ed6\u3ed7\u3ed8\u3ed9\u3eda\u3edb\u3edc\u3edd\u3ede\u3edf\u3ee0\u3ee1\u3ee2\u3ee3\u3ee4\u3ee5\u3ee6\u3ee7\u3ee8\u3ee9\u3eea\u3eeb\u3eec\u3eed\u3eee\u3eef\u3ef0\u3ef1\u3ef2\u3ef3\u3ef4\u3ef5\u3ef6\u3ef7\u3ef8\u3ef9\u3efa\u3efb\u3efc\u3efd\u3efe\u3eff\u3f00\u3f01\u3f02\u3f03\u3f04\u3f05\u3f06\u3f07\u3f08\u3f09\u3f0a\u3f0b\u3f0c\u3f0d\u3f0e\u3f0f\u3f10\u3f11\u3f12\u3f13\u3f14\u3f15\u3f16\u3f17\u3f18\u3f19\u3f1a\u3f1b\u3f1c\u3f1d\u3f1e\u3f1f\u3f20\u3f21\u3f22\u3f23\u3f24\u3f25\u3f26\u3f27\u3f28\u3f29\u3f2a\u3f2b\u3f2c\u3f2d\u3f2e\u3f2f\u3f30\u3f31\u3f32\u3f33\u3f34\u3f35\u3f36\u3f37\u3f38\u3f39\u3f3a\u3f3b\u3f3c\u3f3d\u3f3e\u3f3f\u3f40\u3f41\u3f42\u3f43\u3f44\u3f45\u3f46\u3f47\u3f48\u3f49\u3f4a\u3f4b\u3f4c\u3f4d\u3f4e\u3f4f\u3f50\u3f51\u3f52\u3f53\u3f54\u3f55\u3f56\u3f57\u3f58\u3f59\u3f5a\u3f5b\u3f5c\u3f5d\u3f5e\u3f5f\u3f60\u3f61\u3f62\u3f63\u3f64\u3f65\u3f66\u3f67\u3f68\u3f69\u3f6a\u3f6b\u3f6c\u3f6d\u3f6e\u3f6f\u3f70\u3f71\u3f72\u3f73\u3f74\u3f75\u3f76\u3f77\u3f78\u3f79\u3f7a\u3f7b\u3f7c\u3f7d\u3f7e\u3f7f\u3f80\u3f81\u3f82\u3f83\u3f84\u3f85\u3f86\u3f87\u3f88\u3f89\u3f8a\u3f8b\u3f8c\u3f8d\u3f8e\u3f8f\u3f90\u3f91\u3f92\u3f93\u3f94\u3f95\u3f96\u3f97\u3f98\u3f99\u3f9a\u3f9b\u3f9c\u3f9d\u3f9e\u3f9f\u3fa0\u3fa1\u3fa2\u3fa3\u3fa4\u3fa5\u3fa6\u3fa7\u3fa8\u3fa9\u3faa\u3fab\u3fac\u3fad\u3fae\u3faf\u3fb0\u3fb1\u3fb2\u3fb3\u3fb4\u3fb5\u3fb6\u3fb7\u3fb8\u3fb9\u3fba\u3fbb\u3fbc\u3fbd\u3fbe\u3fbf\u3fc0\u3fc1\u3fc2\u3fc3\u3fc4\u3fc5\u3fc6\u3fc7\u3fc8\u3fc9\u3fca\u3fcb\u3fcc\u3fcd\u3fce\u3fcf\u3fd0\u3fd1\u3fd2\u3fd3\u3fd4\u3fd5\u3fd6\u3fd7\u3fd8\u3fd9\u3fda\u3fdb\u3fdc\u3fdd\u3fde\u3fdf\u3fe0\u3fe1\u3fe2\u3fe3\u3fe4\u3fe5\u3fe6\u3fe7\u3fe8\u3fe9\u3fea\u3feb\u3fec\u3fed\u3fee\u3fef\u3ff0\u3ff1\u3ff2\u3ff3\u3ff4\u3ff5\u3ff6\u3ff7\u3ff8\u3ff9\u3ffa\u3ffb\u3ffc\u3ffd\u3ffe\u3fff\u4000\u4001\u4002\u4003\u4004\u4005\u4006\u4007\u4008\u4009\u400a\u400b\u400c\u400d\u400e\u400f\u4010\u4011\u4012\u4013\u4014\u4015\u4016\u4017\u4018\u4019\u401a\u401b\u401c\u401d\u401e\u401f\u4020\u4021\u4022\u4023\u4024\u4025\u4026\u4027\u4028\u4029\u402a\u402b\u402c\u402d\u402e\u402f\u4030\u4031\u4032\u4033\u4034\u4035\u4036\u4037\u4038\u4039\u403a\u403b\u403c\u403d\u403e\u403f\u4040\u4041\u4042\u4043\u4044\u4045\u4046\u4047\u4048\u4049\u404a\u404b\u404c\u404d\u404e\u404f\u4050\u4051\u4052\u4053\u4054\u4055\u4056\u4057\u4058\u4059\u405a\u405b\u405c\u405d\u405e\u405f\u4060\u4061\u4062\u4063\u4064\u4065\u4066\u4067\u4068\u4069\u406a\u406b\u406c\u406d\u406e\u406f\u4070\u4071\u4072\u4073\u4074\u4075\u4076\u4077\u4078\u4079\u407a\u407b\u407c\u407d\u407e\u407f\u4080\u4081\u4082\u4083\u4084\u4085\u4086\u4087\u4088\u4089\u408a\u408b\u408c\u408d\u408e\u408f\u4090\u4091\u4092\u4093\u4094\u4095\u4096\u4097\u4098\u4099\u409a\u409b\u409c\u409d\u409e\u409f\u40a0\u40a1\u40a2\u40a3\u40a4\u40a5\u40a6\u40a7\u40a8\u40a9\u40aa\u40ab\u40ac\u40ad\u40ae\u40af\u40b0\u40b1\u40b2\u40b3\u40b4\u40b5\u40b6\u40b7\u40b8\u40b9\u40ba\u40bb\u40bc\u40bd\u40be\u40bf\u40c0\u40c1\u40c2\u40c3\u40c4\u40c5\u40c6\u40c7\u40c8\u40c9\u40ca\u40cb\u40cc\u40cd\u40ce\u40cf\u40d0\u40d1\u40d2\u40d3\u40d4\u40d5\u40d6\u40d7\u40d8\u40d9\u40da\u40db\u40dc\u40dd\u40de\u40df\u40e0\u40e1\u40e2\u40e3\u40e4\u40e5\u40e6\u40e7\u40e8\u40e9\u40ea\u40eb\u40ec\u40ed\u40ee\u40ef\u40f0\u40f1\u40f2\u40f3\u40f4\u40f5\u40f6\u40f7\u40f8\u40f9\u40fa\u40fb\u40fc\u40fd\u40fe\u40ff\u4100\u4101\u4102\u4103\u4104\u4105\u4106\u4107\u4108\u4109\u410a\u410b\u410c\u410d\u410e\u410f\u4110\u4111\u4112\u4113\u4114\u4115\u4116\u4117\u4118\u4119\u411a\u411b\u411c\u411d\u411e\u411f\u4120\u4121\u4122\u4123\u4124\u4125\u4126\u4127\u4128\u4129\u412a\u412b\u412c\u412d\u412e\u412f\u4130\u4131\u4132\u4133\u4134\u4135\u4136\u4137\u4138\u4139\u413a\u413b\u413c\u413d\u413e\u413f\u4140\u4141\u4142\u4143\u4144\u4145\u4146\u4147\u4148\u4149\u414a\u414b\u414c\u414d\u414e\u414f\u4150\u4151\u4152\u4153\u4154\u4155\u4156\u4157\u4158\u4159\u415a\u415b\u415c\u415d\u415e\u415f\u4160\u4161\u4162\u4163\u4164\u4165\u4166\u4167\u4168\u4169\u416a\u416b\u416c\u416d\u416e\u416f\u4170\u4171\u4172\u4173\u4174\u4175\u4176\u4177\u4178\u4179\u417a\u417b\u417c\u417d\u417e\u417f\u4180\u4181\u4182\u4183\u4184\u4185\u4186\u4187\u4188\u4189\u418a\u418b\u418c\u418d\u418e\u418f\u4190\u4191\u4192\u4193\u4194\u4195\u4196\u4197\u4198\u4199\u419a\u419b\u419c\u419d\u419e\u419f\u41a0\u41a1\u41a2\u41a3\u41a4\u41a5\u41a6\u41a7\u41a8\u41a9\u41aa\u41ab\u41ac\u41ad\u41ae\u41af\u41b0\u41b1\u41b2\u41b3\u41b4\u41b5\u41b6\u41b7\u41b8\u41b9\u41ba\u41bb\u41bc\u41bd\u41be\u41bf\u41c0\u41c1\u41c2\u41c3\u41c4\u41c5\u41c6\u41c7\u41c8\u41c9\u41ca\u41cb\u41cc\u41cd\u41ce\u41cf\u41d0\u41d1\u41d2\u41d3\u41d4\u41d5\u41d6\u41d7\u41d8\u41d9\u41da\u41db\u41dc\u41dd\u41de\u41df\u41e0\u41e1\u41e2\u41e3\u41e4\u41e5\u41e6\u41e7\u41e8\u41e9\u41ea\u41eb\u41ec\u41ed\u41ee\u41ef\u41f0\u41f1\u41f2\u41f3\u41f4\u41f5\u41f6\u41f7\u41f8\u41f9\u41fa\u41fb\u41fc\u41fd\u41fe\u41ff\u4200\u4201\u4202\u4203\u4204\u4205\u4206\u4207\u4208\u4209\u420a\u420b\u420c\u420d\u420e\u420f\u4210\u4211\u4212\u4213\u4214\u4215\u4216\u4217\u4218\u4219\u421a\u421b\u421c\u421d\u421e\u421f\u4220\u4221\u4222\u4223\u4224\u4225\u4226\u4227\u4228\u4229\u422a\u422b\u422c\u422d\u422e\u422f\u4230\u4231\u4232\u4233\u4234\u4235\u4236\u4237\u4238\u4239\u423a\u423b\u423c\u423d\u423e\u423f\u4240\u4241\u4242\u4243\u4244\u4245\u4246\u4247\u4248\u4249\u424a\u424b\u424c\u424d\u424e\u424f\u4250\u4251\u4252\u4253\u4254\u4255\u4256\u4257\u4258\u4259\u425a\u425b\u425c\u425d\u425e\u425f\u4260\u4261\u4262\u4263\u4264\u4265\u4266\u4267\u4268\u4269\u426a\u426b\u426c\u426d\u426e\u426f\u4270\u4271\u4272\u4273\u4274\u4275\u4276\u4277\u4278\u4279\u427a\u427b\u427c\u427d\u427e\u427f\u4280\u4281\u4282\u4283\u4284\u4285\u4286\u4287\u4288\u4289\u428a\u428b\u428c\u428d\u428e\u428f\u4290\u4291\u4292\u4293\u4294\u4295\u4296\u4297\u4298\u4299\u429a\u429b\u429c\u429d\u429e\u429f\u42a0\u42a1\u42a2\u42a3\u42a4\u42a5\u42a6\u42a7\u42a8\u42a9\u42aa\u42ab\u42ac\u42ad\u42ae\u42af\u42b0\u42b1\u42b2\u42b3\u42b4\u42b5\u42b6\u42b7\u42b8\u42b9\u42ba\u42bb\u42bc\u42bd\u42be\u42bf\u42c0\u42c1\u42c2\u42c3\u42c4\u42c5\u42c6\u42c7\u42c8\u42c9\u42ca\u42cb\u42cc\u42cd\u42ce\u42cf\u42d0\u42d1\u42d2\u42d3\u42d4\u42d5\u42d6\u42d7\u42d8\u42d9\u42da\u42db\u42dc\u42dd\u42de\u42df\u42e0\u42e1\u42e2\u42e3\u42e4\u42e5\u42e6\u42e7\u42e8\u42e9\u42ea\u42eb\u42ec\u42ed\u42ee\u42ef\u42f0\u42f1\u42f2\u42f3\u42f4\u42f5\u42f6\u42f7\u42f8\u42f9\u42fa\u42fb\u42fc\u42fd\u42fe\u42ff\u4300\u4301\u4302\u4303\u4304\u4305\u4306\u4307\u4308\u4309\u430a\u430b\u430c\u430d\u430e\u430f\u4310\u4311\u4312\u4313\u4314\u4315\u4316\u4317\u4318\u4319\u431a\u431b\u431c\u431d\u431e\u431f\u4320\u4321\u4322\u4323\u4324\u4325\u4326\u4327\u4328\u4329\u432a\u432b\u432c\u432d\u432e\u432f\u4330\u4331\u4332\u4333\u4334\u4335\u4336\u4337\u4338\u4339\u433a\u433b\u433c\u433d\u433e\u433f\u4340\u4341\u4342\u4343\u4344\u4345\u4346\u4347\u4348\u4349\u434a\u434b\u434c\u434d\u434e\u434f\u4350\u4351\u4352\u4353\u4354\u4355\u4356\u4357\u4358\u4359\u435a\u435b\u435c\u435d\u435e\u435f\u4360\u4361\u4362\u4363\u4364\u4365\u4366\u4367\u4368\u4369\u436a\u436b\u436c\u436d\u436e\u436f\u4370\u4371\u4372\u4373\u4374\u4375\u4376\u4377\u4378\u4379\u437a\u437b\u437c\u437d\u437e\u437f\u4380\u4381\u4382\u4383\u4384\u4385\u4386\u4387\u4388\u4389\u438a\u438b\u438c\u438d\u438e\u438f\u4390\u4391\u4392\u4393\u4394\u4395\u4396\u4397\u4398\u4399\u439a\u439b\u439c\u439d\u439e\u439f\u43a0\u43a1\u43a2\u43a3\u43a4\u43a5\u43a6\u43a7\u43a8\u43a9\u43aa\u43ab\u43ac\u43ad\u43ae\u43af\u43b0\u43b1\u43b2\u43b3\u43b4\u43b5\u43b6\u43b7\u43b8\u43b9\u43ba\u43bb\u43bc\u43bd\u43be\u43bf\u43c0\u43c1\u43c2\u43c3\u43c4\u43c5\u43c6\u43c7\u43c8\u43c9\u43ca\u43cb\u43cc\u43cd\u43ce\u43cf\u43d0\u43d1\u43d2\u43d3\u43d4\u43d5\u43d6\u43d7\u43d8\u43d9\u43da\u43db\u43dc\u43dd\u43de\u43df\u43e0\u43e1\u43e2\u43e3\u43e4\u43e5\u43e6\u43e7\u43e8\u43e9\u43ea\u43eb\u43ec\u43ed\u43ee\u43ef\u43f0\u43f1\u43f2\u43f3\u43f4\u43f5\u43f6\u43f7\u43f8\u43f9\u43fa\u43fb\u43fc\u43fd\u43fe\u43ff\u4400\u4401\u4402\u4403\u4404\u4405\u4406\u4407\u4408\u4409\u440a\u440b\u440c\u440d\u440e\u440f\u4410\u4411\u4412\u4413\u4414\u4415\u4416\u4417\u4418\u4419\u441a\u441b\u441c\u441d\u441e\u441f\u4420\u4421\u4422\u4423\u4424\u4425\u4426\u4427\u4428\u4429\u442a\u442b\u442c\u442d\u442e\u442f\u4430\u4431\u4432\u4433\u4434\u4435\u4436\u4437\u4438\u4439\u443a\u443b\u443c\u443d\u443e\u443f\u4440\u4441\u4442\u4443\u4444\u4445\u4446\u4447\u4448\u4449\u444a\u444b\u444c\u444d\u444e\u444f\u4450\u4451\u4452\u4453\u4454\u4455\u4456\u4457\u4458\u4459\u445a\u445b\u445c\u445d\u445e\u445f\u4460\u4461\u4462\u4463\u4464\u4465\u4466\u4467\u4468\u4469\u446a\u446b\u446c\u446d\u446e\u446f\u4470\u4471\u4472\u4473\u4474\u4475\u4476\u4477\u4478\u4479\u447a\u447b\u447c\u447d\u447e\u447f\u4480\u4481\u4482\u4483\u4484\u4485\u4486\u4487\u4488\u4489\u448a\u448b\u448c\u448d\u448e\u448f\u4490\u4491\u4492\u4493\u4494\u4495\u4496\u4497\u4498\u4499\u449a\u449b\u449c\u449d\u449e\u449f\u44a0\u44a1\u44a2\u44a3\u44a4\u44a5\u44a6\u44a7\u44a8\u44a9\u44aa\u44ab\u44ac\u44ad\u44ae\u44af\u44b0\u44b1\u44b2\u44b3\u44b4\u44b5\u44b6\u44b7\u44b8\u44b9\u44ba\u44bb\u44bc\u44bd\u44be\u44bf\u44c0\u44c1\u44c2\u44c3\u44c4\u44c5\u44c6\u44c7\u44c8\u44c9\u44ca\u44cb\u44cc\u44cd\u44ce\u44cf\u44d0\u44d1\u44d2\u44d3\u44d4\u44d5\u44d6\u44d7\u44d8\u44d9\u44da\u44db\u44dc\u44dd\u44de\u44df\u44e0\u44e1\u44e2\u44e3\u44e4\u44e5\u44e6\u44e7\u44e8\u44e9\u44ea\u44eb\u44ec\u44ed\u44ee\u44ef\u44f0\u44f1\u44f2\u44f3\u44f4\u44f5\u44f6\u44f7\u44f8\u44f9\u44fa\u44fb\u44fc\u44fd\u44fe\u44ff\u4500\u4501\u4502\u4503\u4504\u4505\u4506\u4507\u4508\u4509\u450a\u450b\u450c\u450d\u450e\u450f\u4510\u4511\u4512\u4513\u4514\u4515\u4516\u4517\u4518\u4519\u451a\u451b\u451c\u451d\u451e\u451f\u4520\u4521\u4522\u4523\u4524\u4525\u4526\u4527\u4528\u4529\u452a\u452b\u452c\u452d\u452e\u452f\u4530\u4531\u4532\u4533\u4534\u4535\u4536\u4537\u4538\u4539\u453a\u453b\u453c\u453d\u453e\u453f\u4540\u4541\u4542\u4543\u4544\u4545\u4546\u4547\u4548\u4549\u454a\u454b\u454c\u454d\u454e\u454f\u4550\u4551\u4552\u4553\u4554\u4555\u4556\u4557\u4558\u4559\u455a\u455b\u455c\u455d\u455e\u455f\u4560\u4561\u4562\u4563\u4564\u4565\u4566\u4567\u4568\u4569\u456a\u456b\u456c\u456d\u456e\u456f\u4570\u4571\u4572\u4573\u4574\u4575\u4576\u4577\u4578\u4579\u457a\u457b\u457c\u457d\u457e\u457f\u4580\u4581\u4582\u4583\u4584\u4585\u4586\u4587\u4588\u4589\u458a\u458b\u458c\u458d\u458e\u458f\u4590\u4591\u4592\u4593\u4594\u4595\u4596\u4597\u4598\u4599\u459a\u459b\u459c\u459d\u459e\u459f\u45a0\u45a1\u45a2\u45a3\u45a4\u45a5\u45a6\u45a7\u45a8\u45a9\u45aa\u45ab\u45ac\u45ad\u45ae\u45af\u45b0\u45b1\u45b2\u45b3\u45b4\u45b5\u45b6\u45b7\u45b8\u45b9\u45ba\u45bb\u45bc\u45bd\u45be\u45bf\u45c0\u45c1\u45c2\u45c3\u45c4\u45c5\u45c6\u45c7\u45c8\u45c9\u45ca\u45cb\u45cc\u45cd\u45ce\u45cf\u45d0\u45d1\u45d2\u45d3\u45d4\u45d5\u45d6\u45d7\u45d8\u45d9\u45da\u45db\u45dc\u45dd\u45de\u45df\u45e0\u45e1\u45e2\u45e3\u45e4\u45e5\u45e6\u45e7\u45e8\u45e9\u45ea\u45eb\u45ec\u45ed\u45ee\u45ef\u45f0\u45f1\u45f2\u45f3\u45f4\u45f5\u45f6\u45f7\u45f8\u45f9\u45fa\u45fb\u45fc\u45fd\u45fe\u45ff\u4600\u4601\u4602\u4603\u4604\u4605\u4606\u4607\u4608\u4609\u460a\u460b\u460c\u460d\u460e\u460f\u4610\u4611\u4612\u4613\u4614\u4615\u4616\u4617\u4618\u4619\u461a\u461b\u461c\u461d\u461e\u461f\u4620\u4621\u4622\u4623\u4624\u4625\u4626\u4627\u4628\u4629\u462a\u462b\u462c\u462d\u462e\u462f\u4630\u4631\u4632\u4633\u4634\u4635\u4636\u4637\u4638\u4639\u463a\u463b\u463c\u463d\u463e\u463f\u4640\u4641\u4642\u4643\u4644\u4645\u4646\u4647\u4648\u4649\u464a\u464b\u464c\u464d\u464e\u464f\u4650\u4651\u4652\u4653\u4654\u4655\u4656\u4657\u4658\u4659\u465a\u465b\u465c\u465d\u465e\u465f\u4660\u4661\u4662\u4663\u4664\u4665\u4666\u4667\u4668\u4669\u466a\u466b\u466c\u466d\u466e\u466f\u4670\u4671\u4672\u4673\u4674\u4675\u4676\u4677\u4678\u4679\u467a\u467b\u467c\u467d\u467e\u467f\u4680\u4681\u4682\u4683\u4684\u4685\u4686\u4687\u4688\u4689\u468a\u468b\u468c\u468d\u468e\u468f\u4690\u4691\u4692\u4693\u4694\u4695\u4696\u4697\u4698\u4699\u469a\u469b\u469c\u469d\u469e\u469f\u46a0\u46a1\u46a2\u46a3\u46a4\u46a5\u46a6\u46a7\u46a8\u46a9\u46aa\u46ab\u46ac\u46ad\u46ae\u46af\u46b0\u46b1\u46b2\u46b3\u46b4\u46b5\u46b6\u46b7\u46b8\u46b9\u46ba\u46bb\u46bc\u46bd\u46be\u46bf\u46c0\u46c1\u46c2\u46c3\u46c4\u46c5\u46c6\u46c7\u46c8\u46c9\u46ca\u46cb\u46cc\u46cd\u46ce\u46cf\u46d0\u46d1\u46d2\u46d3\u46d4\u46d5\u46d6\u46d7\u46d8\u46d9\u46da\u46db\u46dc\u46dd\u46de\u46df\u46e0\u46e1\u46e2\u46e3\u46e4\u46e5\u46e6\u46e7\u46e8\u46e9\u46ea\u46eb\u46ec\u46ed\u46ee\u46ef\u46f0\u46f1\u46f2\u46f3\u46f4\u46f5\u46f6\u46f7\u46f8\u46f9\u46fa\u46fb\u46fc\u46fd\u46fe\u46ff\u4700\u4701\u4702\u4703\u4704\u4705\u4706\u4707\u4708\u4709\u470a\u470b\u470c\u470d\u470e\u470f\u4710\u4711\u4712\u4713\u4714\u4715\u4716\u4717\u4718\u4719\u471a\u471b\u471c\u471d\u471e\u471f\u4720\u4721\u4722\u4723\u4724\u4725\u4726\u4727\u4728\u4729\u472a\u472b\u472c\u472d\u472e\u472f\u4730\u4731\u4732\u4733\u4734\u4735\u4736\u4737\u4738\u4739\u473a\u473b\u473c\u473d\u473e\u473f\u4740\u4741\u4742\u4743\u4744\u4745\u4746\u4747\u4748\u4749\u474a\u474b\u474c\u474d\u474e\u474f\u4750\u4751\u4752\u4753\u4754\u4755\u4756\u4757\u4758\u4759\u475a\u475b\u475c\u475d\u475e\u475f\u4760\u4761\u4762\u4763\u4764\u4765\u4766\u4767\u4768\u4769\u476a\u476b\u476c\u476d\u476e\u476f\u4770\u4771\u4772\u4773\u4774\u4775\u4776\u4777\u4778\u4779\u477a\u477b\u477c\u477d\u477e\u477f\u4780\u4781\u4782\u4783\u4784\u4785\u4786\u4787\u4788\u4789\u478a\u478b\u478c\u478d\u478e\u478f\u4790\u4791\u4792\u4793\u4794\u4795\u4796\u4797\u4798\u4799\u479a\u479b\u479c\u479d\u479e\u479f\u47a0\u47a1\u47a2\u47a3\u47a4\u47a5\u47a6\u47a7\u47a8\u47a9\u47aa\u47ab\u47ac\u47ad\u47ae\u47af\u47b0\u47b1\u47b2\u47b3\u47b4\u47b5\u47b6\u47b7\u47b8\u47b9\u47ba\u47bb\u47bc\u47bd\u47be\u47bf\u47c0\u47c1\u47c2\u47c3\u47c4\u47c5\u47c6\u47c7\u47c8\u47c9\u47ca\u47cb\u47cc\u47cd\u47ce\u47cf\u47d0\u47d1\u47d2\u47d3\u47d4\u47d5\u47d6\u47d7\u47d8\u47d9\u47da\u47db\u47dc\u47dd\u47de\u47df\u47e0\u47e1\u47e2\u47e3\u47e4\u47e5\u47e6\u47e7\u47e8\u47e9\u47ea\u47eb\u47ec\u47ed\u47ee\u47ef\u47f0\u47f1\u47f2\u47f3\u47f4\u47f5\u47f6\u47f7\u47f8\u47f9\u47fa\u47fb\u47fc\u47fd\u47fe\u47ff\u4800\u4801\u4802\u4803\u4804\u4805\u4806\u4807\u4808\u4809\u480a\u480b\u480c\u480d\u480e\u480f\u4810\u4811\u4812\u4813\u4814\u4815\u4816\u4817\u4818\u4819\u481a\u481b\u481c\u481d\u481e\u481f\u4820\u4821\u4822\u4823\u4824\u4825\u4826\u4827\u4828\u4829\u482a\u482b\u482c\u482d\u482e\u482f\u4830\u4831\u4832\u4833\u4834\u4835\u4836\u4837\u4838\u4839\u483a\u483b\u483c\u483d\u483e\u483f\u4840\u4841\u4842\u4843\u4844\u4845\u4846\u4847\u4848\u4849\u484a\u484b\u484c\u484d\u484e\u484f\u4850\u4851\u4852\u4853\u4854\u4855\u4856\u4857\u4858\u4859\u485a\u485b\u485c\u485d\u485e\u485f\u4860\u4861\u4862\u4863\u4864\u4865\u4866\u4867\u4868\u4869\u486a\u486b\u486c\u486d\u486e\u486f\u4870\u4871\u4872\u4873\u4874\u4875\u4876\u4877\u4878\u4879\u487a\u487b\u487c\u487d\u487e\u487f\u4880\u4881\u4882\u4883\u4884\u4885\u4886\u4887\u4888\u4889\u488a\u488b\u488c\u488d\u488e\u488f\u4890\u4891\u4892\u4893\u4894\u4895\u4896\u4897\u4898\u4899\u489a\u489b\u489c\u489d\u489e\u489f\u48a0\u48a1\u48a2\u48a3\u48a4\u48a5\u48a6\u48a7\u48a8\u48a9\u48aa\u48ab\u48ac\u48ad\u48ae\u48af\u48b0\u48b1\u48b2\u48b3\u48b4\u48b5\u48b6\u48b7\u48b8\u48b9\u48ba\u48bb\u48bc\u48bd\u48be\u48bf\u48c0\u48c1\u48c2\u48c3\u48c4\u48c5\u48c6\u48c7\u48c8\u48c9\u48ca\u48cb\u48cc\u48cd\u48ce\u48cf\u48d0\u48d1\u48d2\u48d3\u48d4\u48d5\u48d6\u48d7\u48d8\u48d9\u48da\u48db\u48dc\u48dd\u48de\u48df\u48e0\u48e1\u48e2\u48e3\u48e4\u48e5\u48e6\u48e7\u48e8\u48e9\u48ea\u48eb\u48ec\u48ed\u48ee\u48ef\u48f0\u48f1\u48f2\u48f3\u48f4\u48f5\u48f6\u48f7\u48f8\u48f9\u48fa\u48fb\u48fc\u48fd\u48fe\u48ff\u4900\u4901\u4902\u4903\u4904\u4905\u4906\u4907\u4908\u4909\u490a\u490b\u490c\u490d\u490e\u490f\u4910\u4911\u4912\u4913\u4914\u4915\u4916\u4917\u4918\u4919\u491a\u491b\u491c\u491d\u491e\u491f\u4920\u4921\u4922\u4923\u4924\u4925\u4926\u4927\u4928\u4929\u492a\u492b\u492c\u492d\u492e\u492f\u4930\u4931\u4932\u4933\u4934\u4935\u4936\u4937\u4938\u4939\u493a\u493b\u493c\u493d\u493e\u493f\u4940\u4941\u4942\u4943\u4944\u4945\u4946\u4947\u4948\u4949\u494a\u494b\u494c\u494d\u494e\u494f\u4950\u4951\u4952\u4953\u4954\u4955\u4956\u4957\u4958\u4959\u495a\u495b\u495c\u495d\u495e\u495f\u4960\u4961\u4962\u4963\u4964\u4965\u4966\u4967\u4968\u4969\u496a\u496b\u496c\u496d\u496e\u496f\u4970\u4971\u4972\u4973\u4974\u4975\u4976\u4977\u4978\u4979\u497a\u497b\u497c\u497d\u497e\u497f\u4980\u4981\u4982\u4983\u4984\u4985\u4986\u4987\u4988\u4989\u498a\u498b\u498c\u498d\u498e\u498f\u4990\u4991\u4992\u4993\u4994\u4995\u4996\u4997\u4998\u4999\u499a\u499b\u499c\u499d\u499e\u499f\u49a0\u49a1\u49a2\u49a3\u49a4\u49a5\u49a6\u49a7\u49a8\u49a9\u49aa\u49ab\u49ac\u49ad\u49ae\u49af\u49b0\u49b1\u49b2\u49b3\u49b4\u49b5\u49b6\u49b7\u49b8\u49b9\u49ba\u49bb\u49bc\u49bd\u49be\u49bf\u49c0\u49c1\u49c2\u49c3\u49c4\u49c5\u49c6\u49c7\u49c8\u49c9\u49ca\u49cb\u49cc\u49cd\u49ce\u49cf\u49d0\u49d1\u49d2\u49d3\u49d4\u49d5\u49d6\u49d7\u49d8\u49d9\u49da\u49db\u49dc\u49dd\u49de\u49df\u49e0\u49e1\u49e2\u49e3\u49e4\u49e5\u49e6\u49e7\u49e8\u49e9\u49ea\u49eb\u49ec\u49ed\u49ee\u49ef\u49f0\u49f1\u49f2\u49f3\u49f4\u49f5\u49f6\u49f7\u49f8\u49f9\u49fa\u49fb\u49fc\u49fd\u49fe\u49ff\u4a00\u4a01\u4a02\u4a03\u4a04\u4a05\u4a06\u4a07\u4a08\u4a09\u4a0a\u4a0b\u4a0c\u4a0d\u4a0e\u4a0f\u4a10\u4a11\u4a12\u4a13\u4a14\u4a15\u4a16\u4a17\u4a18\u4a19\u4a1a\u4a1b\u4a1c\u4a1d\u4a1e\u4a1f\u4a20\u4a21\u4a22\u4a23\u4a24\u4a25\u4a26\u4a27\u4a28\u4a29\u4a2a\u4a2b\u4a2c\u4a2d\u4a2e\u4a2f\u4a30\u4a31\u4a32\u4a33\u4a34\u4a35\u4a36\u4a37\u4a38\u4a39\u4a3a\u4a3b\u4a3c\u4a3d\u4a3e\u4a3f\u4a40\u4a41\u4a42\u4a43\u4a44\u4a45\u4a46\u4a47\u4a48\u4a49\u4a4a\u4a4b\u4a4c\u4a4d\u4a4e\u4a4f\u4a50\u4a51\u4a52\u4a53\u4a54\u4a55\u4a56\u4a57\u4a58\u4a59\u4a5a\u4a5b\u4a5c\u4a5d\u4a5e\u4a5f\u4a60\u4a61\u4a62\u4a63\u4a64\u4a65\u4a66\u4a67\u4a68\u4a69\u4a6a\u4a6b\u4a6c\u4a6d\u4a6e\u4a6f\u4a70\u4a71\u4a72\u4a73\u4a74\u4a75\u4a76\u4a77\u4a78\u4a79\u4a7a\u4a7b\u4a7c\u4a7d\u4a7e\u4a7f\u4a80\u4a81\u4a82\u4a83\u4a84\u4a85\u4a86\u4a87\u4a88\u4a89\u4a8a\u4a8b\u4a8c\u4a8d\u4a8e\u4a8f\u4a90\u4a91\u4a92\u4a93\u4a94\u4a95\u4a96\u4a97\u4a98\u4a99\u4a9a\u4a9b\u4a9c\u4a9d\u4a9e\u4a9f\u4aa0\u4aa1\u4aa2\u4aa3\u4aa4\u4aa5\u4aa6\u4aa7\u4aa8\u4aa9\u4aaa\u4aab\u4aac\u4aad\u4aae\u4aaf\u4ab0\u4ab1\u4ab2\u4ab3\u4ab4\u4ab5\u4ab6\u4ab7\u4ab8\u4ab9\u4aba\u4abb\u4abc\u4abd\u4abe\u4abf\u4ac0\u4ac1\u4ac2\u4ac3\u4ac4\u4ac5\u4ac6\u4ac7\u4ac8\u4ac9\u4aca\u4acb\u4acc\u4acd\u4ace\u4acf\u4ad0\u4ad1\u4ad2\u4ad3\u4ad4\u4ad5\u4ad6\u4ad7\u4ad8\u4ad9\u4ada\u4adb\u4adc\u4add\u4ade\u4adf\u4ae0\u4ae1\u4ae2\u4ae3\u4ae4\u4ae5\u4ae6\u4ae7\u4ae8\u4ae9\u4aea\u4aeb\u4aec\u4aed\u4aee\u4aef\u4af0\u4af1\u4af2\u4af3\u4af4\u4af5\u4af6\u4af7\u4af8\u4af9\u4afa\u4afb\u4afc\u4afd\u4afe\u4aff\u4b00\u4b01\u4b02\u4b03\u4b04\u4b05\u4b06\u4b07\u4b08\u4b09\u4b0a\u4b0b\u4b0c\u4b0d\u4b0e\u4b0f\u4b10\u4b11\u4b12\u4b13\u4b14\u4b15\u4b16\u4b17\u4b18\u4b19\u4b1a\u4b1b\u4b1c\u4b1d\u4b1e\u4b1f\u4b20\u4b21\u4b22\u4b23\u4b24\u4b25\u4b26\u4b27\u4b28\u4b29\u4b2a\u4b2b\u4b2c\u4b2d\u4b2e\u4b2f\u4b30\u4b31\u4b32\u4b33\u4b34\u4b35\u4b36\u4b37\u4b38\u4b39\u4b3a\u4b3b\u4b3c\u4b3d\u4b3e\u4b3f\u4b40\u4b41\u4b42\u4b43\u4b44\u4b45\u4b46\u4b47\u4b48\u4b49\u4b4a\u4b4b\u4b4c\u4b4d\u4b4e\u4b4f\u4b50\u4b51\u4b52\u4b53\u4b54\u4b55\u4b56\u4b57\u4b58\u4b59\u4b5a\u4b5b\u4b5c\u4b5d\u4b5e\u4b5f\u4b60\u4b61\u4b62\u4b63\u4b64\u4b65\u4b66\u4b67\u4b68\u4b69\u4b6a\u4b6b\u4b6c\u4b6d\u4b6e\u4b6f\u4b70\u4b71\u4b72\u4b73\u4b74\u4b75\u4b76\u4b77\u4b78\u4b79\u4b7a\u4b7b\u4b7c\u4b7d\u4b7e\u4b7f\u4b80\u4b81\u4b82\u4b83\u4b84\u4b85\u4b86\u4b87\u4b88\u4b89\u4b8a\u4b8b\u4b8c\u4b8d\u4b8e\u4b8f\u4b90\u4b91\u4b92\u4b93\u4b94\u4b95\u4b96\u4b97\u4b98\u4b99\u4b9a\u4b9b\u4b9c\u4b9d\u4b9e\u4b9f\u4ba0\u4ba1\u4ba2\u4ba3\u4ba4\u4ba5\u4ba6\u4ba7\u4ba8\u4ba9\u4baa\u4bab\u4bac\u4bad\u4bae\u4baf\u4bb0\u4bb1\u4bb2\u4bb3\u4bb4\u4bb5\u4bb6\u4bb7\u4bb8\u4bb9\u4bba\u4bbb\u4bbc\u4bbd\u4bbe\u4bbf\u4bc0\u4bc1\u4bc2\u4bc3\u4bc4\u4bc5\u4bc6\u4bc7\u4bc8\u4bc9\u4bca\u4bcb\u4bcc\u4bcd\u4bce\u4bcf\u4bd0\u4bd1\u4bd2\u4bd3\u4bd4\u4bd5\u4bd6\u4bd7\u4bd8\u4bd9\u4bda\u4bdb\u4bdc\u4bdd\u4bde\u4bdf\u4be0\u4be1\u4be2\u4be3\u4be4\u4be5\u4be6\u4be7\u4be8\u4be9\u4bea\u4beb\u4bec\u4bed\u4bee\u4bef\u4bf0\u4bf1\u4bf2\u4bf3\u4bf4\u4bf5\u4bf6\u4bf7\u4bf8\u4bf9\u4bfa\u4bfb\u4bfc\u4bfd\u4bfe\u4bff\u4c00\u4c01\u4c02\u4c03\u4c04\u4c05\u4c06\u4c07\u4c08\u4c09\u4c0a\u4c0b\u4c0c\u4c0d\u4c0e\u4c0f\u4c10\u4c11\u4c12\u4c13\u4c14\u4c15\u4c16\u4c17\u4c18\u4c19\u4c1a\u4c1b\u4c1c\u4c1d\u4c1e\u4c1f\u4c20\u4c21\u4c22\u4c23\u4c24\u4c25\u4c26\u4c27\u4c28\u4c29\u4c2a\u4c2b\u4c2c\u4c2d\u4c2e\u4c2f\u4c30\u4c31\u4c32\u4c33\u4c34\u4c35\u4c36\u4c37\u4c38\u4c39\u4c3a\u4c3b\u4c3c\u4c3d\u4c3e\u4c3f\u4c40\u4c41\u4c42\u4c43\u4c44\u4c45\u4c46\u4c47\u4c48\u4c49\u4c4a\u4c4b\u4c4c\u4c4d\u4c4e\u4c4f\u4c50\u4c51\u4c52\u4c53\u4c54\u4c55\u4c56\u4c57\u4c58\u4c59\u4c5a\u4c5b\u4c5c\u4c5d\u4c5e\u4c5f\u4c60\u4c61\u4c62\u4c63\u4c64\u4c65\u4c66\u4c67\u4c68\u4c69\u4c6a\u4c6b\u4c6c\u4c6d\u4c6e\u4c6f\u4c70\u4c71\u4c72\u4c73\u4c74\u4c75\u4c76\u4c77\u4c78\u4c79\u4c7a\u4c7b\u4c7c\u4c7d\u4c7e\u4c7f\u4c80\u4c81\u4c82\u4c83\u4c84\u4c85\u4c86\u4c87\u4c88\u4c89\u4c8a\u4c8b\u4c8c\u4c8d\u4c8e\u4c8f\u4c90\u4c91\u4c92\u4c93\u4c94\u4c95\u4c96\u4c97\u4c98\u4c99\u4c9a\u4c9b\u4c9c\u4c9d\u4c9e\u4c9f\u4ca0\u4ca1\u4ca2\u4ca3\u4ca4\u4ca5\u4ca6\u4ca7\u4ca8\u4ca9\u4caa\u4cab\u4cac\u4cad\u4cae\u4caf\u4cb0\u4cb1\u4cb2\u4cb3\u4cb4\u4cb5\u4cb6\u4cb7\u4cb8\u4cb9\u4cba\u4cbb\u4cbc\u4cbd\u4cbe\u4cbf\u4cc0\u4cc1\u4cc2\u4cc3\u4cc4\u4cc5\u4cc6\u4cc7\u4cc8\u4cc9\u4cca\u4ccb\u4ccc\u4ccd\u4cce\u4ccf\u4cd0\u4cd1\u4cd2\u4cd3\u4cd4\u4cd5\u4cd6\u4cd7\u4cd8\u4cd9\u4cda\u4cdb\u4cdc\u4cdd\u4cde\u4cdf\u4ce0\u4ce1\u4ce2\u4ce3\u4ce4\u4ce5\u4ce6\u4ce7\u4ce8\u4ce9\u4cea\u4ceb\u4cec\u4ced\u4cee\u4cef\u4cf0\u4cf1\u4cf2\u4cf3\u4cf4\u4cf5\u4cf6\u4cf7\u4cf8\u4cf9\u4cfa\u4cfb\u4cfc\u4cfd\u4cfe\u4cff\u4d00\u4d01\u4d02\u4d03\u4d04\u4d05\u4d06\u4d07\u4d08\u4d09\u4d0a\u4d0b\u4d0c\u4d0d\u4d0e\u4d0f\u4d10\u4d11\u4d12\u4d13\u4d14\u4d15\u4d16\u4d17\u4d18\u4d19\u4d1a\u4d1b\u4d1c\u4d1d\u4d1e\u4d1f\u4d20\u4d21\u4d22\u4d23\u4d24\u4d25\u4d26\u4d27\u4d28\u4d29\u4d2a\u4d2b\u4d2c\u4d2d\u4d2e\u4d2f\u4d30\u4d31\u4d32\u4d33\u4d34\u4d35\u4d36\u4d37\u4d38\u4d39\u4d3a\u4d3b\u4d3c\u4d3d\u4d3e\u4d3f\u4d40\u4d41\u4d42\u4d43\u4d44\u4d45\u4d46\u4d47\u4d48\u4d49\u4d4a\u4d4b\u4d4c\u4d4d\u4d4e\u4d4f\u4d50\u4d51\u4d52\u4d53\u4d54\u4d55\u4d56\u4d57\u4d58\u4d59\u4d5a\u4d5b\u4d5c\u4d5d\u4d5e\u4d5f\u4d60\u4d61\u4d62\u4d63\u4d64\u4d65\u4d66\u4d67\u4d68\u4d69\u4d6a\u4d6b\u4d6c\u4d6d\u4d6e\u4d6f\u4d70\u4d71\u4d72\u4d73\u4d74\u4d75\u4d76\u4d77\u4d78\u4d79\u4d7a\u4d7b\u4d7c\u4d7d\u4d7e\u4d7f\u4d80\u4d81\u4d82\u4d83\u4d84\u4d85\u4d86\u4d87\u4d88\u4d89\u4d8a\u4d8b\u4d8c\u4d8d\u4d8e\u4d8f\u4d90\u4d91\u4d92\u4d93\u4d94\u4d95\u4d96\u4d97\u4d98\u4d99\u4d9a\u4d9b\u4d9c\u4d9d\u4d9e\u4d9f\u4da0\u4da1\u4da2\u4da3\u4da4\u4da5\u4da6\u4da7\u4da8\u4da9\u4daa\u4dab\u4dac\u4dad\u4dae\u4daf\u4db0\u4db1\u4db2\u4db3\u4db4\u4db5\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05\u4e06\u4e07\u4e08\u4e09\u4e0a\u4e0b\u4e0c\u4e0d\u4e0e\u4e0f\u4e10\u4e11\u4e12\u4e13\u4e14\u4e15\u4e16\u4e17\u4e18\u4e19\u4e1a\u4e1b\u4e1c\u4e1d\u4e1e\u4e1f\u4e20\u4e21\u4e22\u4e23\u4e24\u4e25\u4e26\u4e27\u4e28\u4e29\u4e2a\u4e2b\u4e2c\u4e2d\u4e2e\u4e2f\u4e30\u4e31\u4e32\u4e33\u4e34\u4e35\u4e36\u4e37\u4e38\u4e39\u4e3a\u4e3b\u4e3c\u4e3d\u4e3e\u4e3f\u4e40\u4e41\u4e42\u4e43\u4e44\u4e45\u4e46\u4e47\u4e48\u4e49\u4e4a\u4e4b\u4e4c\u4e4d\u4e4e\u4e4f\u4e50\u4e51\u4e52\u4e53\u4e54\u4e55\u4e56\u4e57\u4e58\u4e59\u4e5a\u4e5b\u4e5c\u4e5d\u4e5e\u4e5f\u4e60\u4e61\u4e62\u4e63\u4e64\u4e65\u4e66\u4e67\u4e68\u4e69\u4e6a\u4e6b\u4e6c\u4e6d\u4e6e\u4e6f\u4e70\u4e71\u4e72\u4e73\u4e74\u4e75\u4e76\u4e77\u4e78\u4e79\u4e7a\u4e7b\u4e7c\u4e7d\u4e7e\u4e7f\u4e80\u4e81\u4e82\u4e83\u4e84\u4e85\u4e86\u4e87\u4e88\u4e89\u4e8a\u4e8b\u4e8c\u4e8d\u4e8e\u4e8f\u4e90\u4e91\u4e92\u4e93\u4e94\u4e95\u4e96\u4e97\u4e98\u4e99\u4e9a\u4e9b\u4e9c\u4e9d\u4e9e\u4e9f\u4ea0\u4ea1\u4ea2\u4ea3\u4ea4\u4ea5\u4ea6\u4ea7\u4ea8\u4ea9\u4eaa\u4eab\u4eac\u4ead\u4eae\u4eaf\u4eb0\u4eb1\u4eb2\u4eb3\u4eb4\u4eb5\u4eb6\u4eb7\u4eb8\u4eb9\u4eba\u4ebb\u4ebc\u4ebd\u4ebe\u4ebf\u4ec0\u4ec1\u4ec2\u4ec3\u4ec4\u4ec5\u4ec6\u4ec7\u4ec8\u4ec9\u4eca\u4ecb\u4ecc\u4ecd\u4ece\u4ecf\u4ed0\u4ed1\u4ed2\u4ed3\u4ed4\u4ed5\u4ed6\u4ed7\u4ed8\u4ed9\u4eda\u4edb\u4edc\u4edd\u4ede\u4edf\u4ee0\u4ee1\u4ee2\u4ee3\u4ee4\u4ee5\u4ee6\u4ee7\u4ee8\u4ee9\u4eea\u4eeb\u4eec\u4eed\u4eee\u4eef\u4ef0\u4ef1\u4ef2\u4ef3\u4ef4\u4ef5\u4ef6\u4ef7\u4ef8\u4ef9\u4efa\u4efb\u4efc\u4efd\u4efe\u4eff\u4f00\u4f01\u4f02\u4f03\u4f04\u4f05\u4f06\u4f07\u4f08\u4f09\u4f0a\u4f0b\u4f0c\u4f0d\u4f0e\u4f0f\u4f10\u4f11\u4f12\u4f13\u4f14\u4f15\u4f16\u4f17\u4f18\u4f19\u4f1a\u4f1b\u4f1c\u4f1d\u4f1e\u4f1f\u4f20\u4f21\u4f22\u4f23\u4f24\u4f25\u4f26\u4f27\u4f28\u4f29\u4f2a\u4f2b\u4f2c\u4f2d\u4f2e\u4f2f\u4f30\u4f31\u4f32\u4f33\u4f34\u4f35\u4f36\u4f37\u4f38\u4f39\u4f3a\u4f3b\u4f3c\u4f3d\u4f3e\u4f3f\u4f40\u4f41\u4f42\u4f43\u4f44\u4f45\u4f46\u4f47\u4f48\u4f49\u4f4a\u4f4b\u4f4c\u4f4d\u4f4e\u4f4f\u4f50\u4f51\u4f52\u4f53\u4f54\u4f55\u4f56\u4f57\u4f58\u4f59\u4f5a\u4f5b\u4f5c\u4f5d\u4f5e\u4f5f\u4f60\u4f61\u4f62\u4f63\u4f64\u4f65\u4f66\u4f67\u4f68\u4f69\u4f6a\u4f6b\u4f6c\u4f6d\u4f6e\u4f6f\u4f70\u4f71\u4f72\u4f73\u4f74\u4f75\u4f76\u4f77\u4f78\u4f79\u4f7a\u4f7b\u4f7c\u4f7d\u4f7e\u4f7f\u4f80\u4f81\u4f82\u4f83\u4f84\u4f85\u4f86\u4f87\u4f88\u4f89\u4f8a\u4f8b\u4f8c\u4f8d\u4f8e\u4f8f\u4f90\u4f91\u4f92\u4f93\u4f94\u4f95\u4f96\u4f97\u4f98\u4f99\u4f9a\u4f9b\u4f9c\u4f9d\u4f9e\u4f9f\u4fa0\u4fa1\u4fa2\u4fa3\u4fa4\u4fa5\u4fa6\u4fa7\u4fa8\u4fa9\u4faa\u4fab\u4fac\u4fad\u4fae\u4faf\u4fb0\u4fb1\u4fb2\u4fb3\u4fb4\u4fb5\u4fb6\u4fb7\u4fb8\u4fb9\u4fba\u4fbb\u4fbc\u4fbd\u4fbe\u4fbf\u4fc0\u4fc1\u4fc2\u4fc3\u4fc4\u4fc5\u4fc6\u4fc7\u4fc8\u4fc9\u4fca\u4fcb\u4fcc\u4fcd\u4fce\u4fcf\u4fd0\u4fd1\u4fd2\u4fd3\u4fd4\u4fd5\u4fd6\u4fd7\u4fd8\u4fd9\u4fda\u4fdb\u4fdc\u4fdd\u4fde\u4fdf\u4fe0\u4fe1\u4fe2\u4fe3\u4fe4\u4fe5\u4fe6\u4fe7\u4fe8\u4fe9\u4fea\u4feb\u4fec\u4fed\u4fee\u4fef\u4ff0\u4ff1\u4ff2\u4ff3\u4ff4\u4ff5\u4ff6\u4ff7\u4ff8\u4ff9\u4ffa\u4ffb\u4ffc\u4ffd\u4ffe\u4fff\u5000\u5001\u5002\u5003\u5004\u5005\u5006\u5007\u5008\u5009\u500a\u500b\u500c\u500d\u500e\u500f\u5010\u5011\u5012\u5013\u5014\u5015\u5016\u5017\u5018\u5019\u501a\u501b\u501c\u501d\u501e\u501f\u5020\u5021\u5022\u5023\u5024\u5025\u5026\u5027\u5028\u5029\u502a\u502b\u502c\u502d\u502e\u502f\u5030\u5031\u5032\u5033\u5034\u5035\u5036\u5037\u5038\u5039\u503a\u503b\u503c\u503d\u503e\u503f\u5040\u5041\u5042\u5043\u5044\u5045\u5046\u5047\u5048\u5049\u504a\u504b\u504c\u504d\u504e\u504f\u5050\u5051\u5052\u5053\u5054\u5055\u5056\u5057\u5058\u5059\u505a\u505b\u505c\u505d\u505e\u505f\u5060\u5061\u5062\u5063\u5064\u5065\u5066\u5067\u5068\u5069\u506a\u506b\u506c\u506d\u506e\u506f\u5070\u5071\u5072\u5073\u5074\u5075\u5076\u5077\u5078\u5079\u507a\u507b\u507c\u507d\u507e\u507f\u5080\u5081\u5082\u5083\u5084\u5085\u5086\u5087\u5088\u5089\u508a\u508b\u508c\u508d\u508e\u508f\u5090\u5091\u5092\u5093\u5094\u5095\u5096\u5097\u5098\u5099\u509a\u509b\u509c\u509d\u509e\u509f\u50a0\u50a1\u50a2\u50a3\u50a4\u50a5\u50a6\u50a7\u50a8\u50a9\u50aa\u50ab\u50ac\u50ad\u50ae\u50af\u50b0\u50b1\u50b2\u50b3\u50b4\u50b5\u50b6\u50b7\u50b8\u50b9\u50ba\u50bb\u50bc\u50bd\u50be\u50bf\u50c0\u50c1\u50c2\u50c3\u50c4\u50c5\u50c6\u50c7\u50c8\u50c9\u50ca\u50cb\u50cc\u50cd\u50ce\u50cf\u50d0\u50d1\u50d2\u50d3\u50d4\u50d5\u50d6\u50d7\u50d8\u50d9\u50da\u50db\u50dc\u50dd\u50de\u50df\u50e0\u50e1\u50e2\u50e3\u50e4\u50e5\u50e6\u50e7\u50e8\u50e9\u50ea\u50eb\u50ec\u50ed\u50ee\u50ef\u50f0\u50f1\u50f2\u50f3\u50f4\u50f5\u50f6\u50f7\u50f8\u50f9\u50fa\u50fb\u50fc\u50fd\u50fe\u50ff\u5100\u5101\u5102\u5103\u5104\u5105\u5106\u5107\u5108\u5109\u510a\u510b\u510c\u510d\u510e\u510f\u5110\u5111\u5112\u5113\u5114\u5115\u5116\u5117\u5118\u5119\u511a\u511b\u511c\u511d\u511e\u511f\u5120\u5121\u5122\u5123\u5124\u5125\u5126\u5127\u5128\u5129\u512a\u512b\u512c\u512d\u512e\u512f\u5130\u5131\u5132\u5133\u5134\u5135\u5136\u5137\u5138\u5139\u513a\u513b\u513c\u513d\u513e\u513f\u5140\u5141\u5142\u5143\u5144\u5145\u5146\u5147\u5148\u5149\u514a\u514b\u514c\u514d\u514e\u514f\u5150\u5151\u5152\u5153\u5154\u5155\u5156\u5157\u5158\u5159\u515a\u515b\u515c\u515d\u515e\u515f\u5160\u5161\u5162\u5163\u5164\u5165\u5166\u5167\u5168\u5169\u516a\u516b\u516c\u516d\u516e\u516f\u5170\u5171\u5172\u5173\u5174\u5175\u5176\u5177\u5178\u5179\u517a\u517b\u517c\u517d\u517e\u517f\u5180\u5181\u5182\u5183\u5184\u5185\u5186\u5187\u5188\u5189\u518a\u518b\u518c\u518d\u518e\u518f\u5190\u5191\u5192\u5193\u5194\u5195\u5196\u5197\u5198\u5199\u519a\u519b\u519c\u519d\u519e\u519f\u51a0\u51a1\u51a2\u51a3\u51a4\u51a5\u51a6\u51a7\u51a8\u51a9\u51aa\u51ab\u51ac\u51ad\u51ae\u51af\u51b0\u51b1\u51b2\u51b3\u51b4\u51b5\u51b6\u51b7\u51b8\u51b9\u51ba\u51bb\u51bc\u51bd\u51be\u51bf\u51c0\u51c1\u51c2\u51c3\u51c4\u51c5\u51c6\u51c7\u51c8\u51c9\u51ca\u51cb\u51cc\u51cd\u51ce\u51cf\u51d0\u51d1\u51d2\u51d3\u51d4\u51d5\u51d6\u51d7\u51d8\u51d9\u51da\u51db\u51dc\u51dd\u51de\u51df\u51e0\u51e1\u51e2\u51e3\u51e4\u51e5\u51e6\u51e7\u51e8\u51e9\u51ea\u51eb\u51ec\u51ed\u51ee\u51ef\u51f0\u51f1\u51f2\u51f3\u51f4\u51f5\u51f6\u51f7\u51f8\u51f9\u51fa\u51fb\u51fc\u51fd\u51fe\u51ff\u5200\u5201\u5202\u5203\u5204\u5205\u5206\u5207\u5208\u5209\u520a\u520b\u520c\u520d\u520e\u520f\u5210\u5211\u5212\u5213\u5214\u5215\u5216\u5217\u5218\u5219\u521a\u521b\u521c\u521d\u521e\u521f\u5220\u5221\u5222\u5223\u5224\u5225\u5226\u5227\u5228\u5229\u522a\u522b\u522c\u522d\u522e\u522f\u5230\u5231\u5232\u5233\u5234\u5235\u5236\u5237\u5238\u5239\u523a\u523b\u523c\u523d\u523e\u523f\u5240\u5241\u5242\u5243\u5244\u5245\u5246\u5247\u5248\u5249\u524a\u524b\u524c\u524d\u524e\u524f\u5250\u5251\u5252\u5253\u5254\u5255\u5256\u5257\u5258\u5259\u525a\u525b\u525c\u525d\u525e\u525f\u5260\u5261\u5262\u5263\u5264\u5265\u5266\u5267\u5268\u5269\u526a\u526b\u526c\u526d\u526e\u526f\u5270\u5271\u5272\u5273\u5274\u5275\u5276\u5277\u5278\u5279\u527a\u527b\u527c\u527d\u527e\u527f\u5280\u5281\u5282\u5283\u5284\u5285\u5286\u5287\u5288\u5289\u528a\u528b\u528c\u528d\u528e\u528f\u5290\u5291\u5292\u5293\u5294\u5295\u5296\u5297\u5298\u5299\u529a\u529b\u529c\u529d\u529e\u529f\u52a0\u52a1\u52a2\u52a3\u52a4\u52a5\u52a6\u52a7\u52a8\u52a9\u52aa\u52ab\u52ac\u52ad\u52ae\u52af\u52b0\u52b1\u52b2\u52b3\u52b4\u52b5\u52b6\u52b7\u52b8\u52b9\u52ba\u52bb\u52bc\u52bd\u52be\u52bf\u52c0\u52c1\u52c2\u52c3\u52c4\u52c5\u52c6\u52c7\u52c8\u52c9\u52ca\u52cb\u52cc\u52cd\u52ce\u52cf\u52d0\u52d1\u52d2\u52d3\u52d4\u52d5\u52d6\u52d7\u52d8\u52d9\u52da\u52db\u52dc\u52dd\u52de\u52df\u52e0\u52e1\u52e2\u52e3\u52e4\u52e5\u52e6\u52e7\u52e8\u52e9\u52ea\u52eb\u52ec\u52ed\u52ee\u52ef\u52f0\u52f1\u52f2\u52f3\u52f4\u52f5\u52f6\u52f7\u52f8\u52f9\u52fa\u52fb\u52fc\u52fd\u52fe\u52ff\u5300\u5301\u5302\u5303\u5304\u5305\u5306\u5307\u5308\u5309\u530a\u530b\u530c\u530d\u530e\u530f\u5310\u5311\u5312\u5313\u5314\u5315\u5316\u5317\u5318\u5319\u531a\u531b\u531c\u531d\u531e\u531f\u5320\u5321\u5322\u5323\u5324\u5325\u5326\u5327\u5328\u5329\u532a\u532b\u532c\u532d\u532e\u532f\u5330\u5331\u5332\u5333\u5334\u5335\u5336\u5337\u5338\u5339\u533a\u533b\u533c\u533d\u533e\u533f\u5340\u5341\u5342\u5343\u5344\u5345\u5346\u5347\u5348\u5349\u534a\u534b\u534c\u534d\u534e\u534f\u5350\u5351\u5352\u5353\u5354\u5355\u5356\u5357\u5358\u5359\u535a\u535b\u535c\u535d\u535e\u535f\u5360\u5361\u5362\u5363\u5364\u5365\u5366\u5367\u5368\u5369\u536a\u536b\u536c\u536d\u536e\u536f\u5370\u5371\u5372\u5373\u5374\u5375\u5376\u5377\u5378\u5379\u537a\u537b\u537c\u537d\u537e\u537f\u5380\u5381\u5382\u5383\u5384\u5385\u5386\u5387\u5388\u5389\u538a\u538b\u538c\u538d\u538e\u538f\u5390\u5391\u5392\u5393\u5394\u5395\u5396\u5397\u5398\u5399\u539a\u539b\u539c\u539d\u539e\u539f\u53a0\u53a1\u53a2\u53a3\u53a4\u53a5\u53a6\u53a7\u53a8\u53a9\u53aa\u53ab\u53ac\u53ad\u53ae\u53af\u53b0\u53b1\u53b2\u53b3\u53b4\u53b5\u53b6\u53b7\u53b8\u53b9\u53ba\u53bb\u53bc\u53bd\u53be\u53bf\u53c0\u53c1\u53c2\u53c3\u53c4\u53c5\u53c6\u53c7\u53c8\u53c9\u53ca\u53cb\u53cc\u53cd\u53ce\u53cf\u53d0\u53d1\u53d2\u53d3\u53d4\u53d5\u53d6\u53d7\u53d8\u53d9\u53da\u53db\u53dc\u53dd\u53de\u53df\u53e0\u53e1\u53e2\u53e3\u53e4\u53e5\u53e6\u53e7\u53e8\u53e9\u53ea\u53eb\u53ec\u53ed\u53ee\u53ef\u53f0\u53f1\u53f2\u53f3\u53f4\u53f5\u53f6\u53f7\u53f8\u53f9\u53fa\u53fb\u53fc\u53fd\u53fe\u53ff\u5400\u5401\u5402\u5403\u5404\u5405\u5406\u5407\u5408\u5409\u540a\u540b\u540c\u540d\u540e\u540f\u5410\u5411\u5412\u5413\u5414\u5415\u5416\u5417\u5418\u5419\u541a\u541b\u541c\u541d\u541e\u541f\u5420\u5421\u5422\u5423\u5424\u5425\u5426\u5427\u5428\u5429\u542a\u542b\u542c\u542d\u542e\u542f\u5430\u5431\u5432\u5433\u5434\u5435\u5436\u5437\u5438\u5439\u543a\u543b\u543c\u543d\u543e\u543f\u5440\u5441\u5442\u5443\u5444\u5445\u5446\u5447\u5448\u5449\u544a\u544b\u544c\u544d\u544e\u544f\u5450\u5451\u5452\u5453\u5454\u5455\u5456\u5457\u5458\u5459\u545a\u545b\u545c\u545d\u545e\u545f\u5460\u5461\u5462\u5463\u5464\u5465\u5466\u5467\u5468\u5469\u546a\u546b\u546c\u546d\u546e\u546f\u5470\u5471\u5472\u5473\u5474\u5475\u5476\u5477\u5478\u5479\u547a\u547b\u547c\u547d\u547e\u547f\u5480\u5481\u5482\u5483\u5484\u5485\u5486\u5487\u5488\u5489\u548a\u548b\u548c\u548d\u548e\u548f\u5490\u5491\u5492\u5493\u5494\u5495\u5496\u5497\u5498\u5499\u549a\u549b\u549c\u549d\u549e\u549f\u54a0\u54a1\u54a2\u54a3\u54a4\u54a5\u54a6\u54a7\u54a8\u54a9\u54aa\u54ab\u54ac\u54ad\u54ae\u54af\u54b0\u54b1\u54b2\u54b3\u54b4\u54b5\u54b6\u54b7\u54b8\u54b9\u54ba\u54bb\u54bc\u54bd\u54be\u54bf\u54c0\u54c1\u54c2\u54c3\u54c4\u54c5\u54c6\u54c7\u54c8\u54c9\u54ca\u54cb\u54cc\u54cd\u54ce\u54cf\u54d0\u54d1\u54d2\u54d3\u54d4\u54d5\u54d6\u54d7\u54d8\u54d9\u54da\u54db\u54dc\u54dd\u54de\u54df\u54e0\u54e1\u54e2\u54e3\u54e4\u54e5\u54e6\u54e7\u54e8\u54e9\u54ea\u54eb\u54ec\u54ed\u54ee\u54ef\u54f0\u54f1\u54f2\u54f3\u54f4\u54f5\u54f6\u54f7\u54f8\u54f9\u54fa\u54fb\u54fc\u54fd\u54fe\u54ff\u5500\u5501\u5502\u5503\u5504\u5505\u5506\u5507\u5508\u5509\u550a\u550b\u550c\u550d\u550e\u550f\u5510\u5511\u5512\u5513\u5514\u5515\u5516\u5517\u5518\u5519\u551a\u551b\u551c\u551d\u551e\u551f\u5520\u5521\u5522\u5523\u5524\u5525\u5526\u5527\u5528\u5529\u552a\u552b\u552c\u552d\u552e\u552f\u5530\u5531\u5532\u5533\u5534\u5535\u5536\u5537\u5538\u5539\u553a\u553b\u553c\u553d\u553e\u553f\u5540\u5541\u5542\u5543\u5544\u5545\u5546\u5547\u5548\u5549\u554a\u554b\u554c\u554d\u554e\u554f\u5550\u5551\u5552\u5553\u5554\u5555\u5556\u5557\u5558\u5559\u555a\u555b\u555c\u555d\u555e\u555f\u5560\u5561\u5562\u5563\u5564\u5565\u5566\u5567\u5568\u5569\u556a\u556b\u556c\u556d\u556e\u556f\u5570\u5571\u5572\u5573\u5574\u5575\u5576\u5577\u5578\u5579\u557a\u557b\u557c\u557d\u557e\u557f\u5580\u5581\u5582\u5583\u5584\u5585\u5586\u5587\u5588\u5589\u558a\u558b\u558c\u558d\u558e\u558f\u5590\u5591\u5592\u5593\u5594\u5595\u5596\u5597\u5598\u5599\u559a\u559b\u559c\u559d\u559e\u559f\u55a0\u55a1\u55a2\u55a3\u55a4\u55a5\u55a6\u55a7\u55a8\u55a9\u55aa\u55ab\u55ac\u55ad\u55ae\u55af\u55b0\u55b1\u55b2\u55b3\u55b4\u55b5\u55b6\u55b7\u55b8\u55b9\u55ba\u55bb\u55bc\u55bd\u55be\u55bf\u55c0\u55c1\u55c2\u55c3\u55c4\u55c5\u55c6\u55c7\u55c8\u55c9\u55ca\u55cb\u55cc\u55cd\u55ce\u55cf\u55d0\u55d1\u55d2\u55d3\u55d4\u55d5\u55d6\u55d7\u55d8\u55d9\u55da\u55db\u55dc\u55dd\u55de\u55df\u55e0\u55e1\u55e2\u55e3\u55e4\u55e5\u55e6\u55e7\u55e8\u55e9\u55ea\u55eb\u55ec\u55ed\u55ee\u55ef\u55f0\u55f1\u55f2\u55f3\u55f4\u55f5\u55f6\u55f7\u55f8\u55f9\u55fa\u55fb\u55fc\u55fd\u55fe\u55ff\u5600\u5601\u5602\u5603\u5604\u5605\u5606\u5607\u5608\u5609\u560a\u560b\u560c\u560d\u560e\u560f\u5610\u5611\u5612\u5613\u5614\u5615\u5616\u5617\u5618\u5619\u561a\u561b\u561c\u561d\u561e\u561f\u5620\u5621\u5622\u5623\u5624\u5625\u5626\u5627\u5628\u5629\u562a\u562b\u562c\u562d\u562e\u562f\u5630\u5631\u5632\u5633\u5634\u5635\u5636\u5637\u5638\u5639\u563a\u563b\u563c\u563d\u563e\u563f\u5640\u5641\u5642\u5643\u5644\u5645\u5646\u5647\u5648\u5649\u564a\u564b\u564c\u564d\u564e\u564f\u5650\u5651\u5652\u5653\u5654\u5655\u5656\u5657\u5658\u5659\u565a\u565b\u565c\u565d\u565e\u565f\u5660\u5661\u5662\u5663\u5664\u5665\u5666\u5667\u5668\u5669\u566a\u566b\u566c\u566d\u566e\u566f\u5670\u5671\u5672\u5673\u5674\u5675\u5676\u5677\u5678\u5679\u567a\u567b\u567c\u567d\u567e\u567f\u5680\u5681\u5682\u5683\u5684\u5685\u5686\u5687\u5688\u5689\u568a\u568b\u568c\u568d\u568e\u568f\u5690\u5691\u5692\u5693\u5694\u5695\u5696\u5697\u5698\u5699\u569a\u569b\u569c\u569d\u569e\u569f\u56a0\u56a1\u56a2\u56a3\u56a4\u56a5\u56a6\u56a7\u56a8\u56a9\u56aa\u56ab\u56ac\u56ad\u56ae\u56af\u56b0\u56b1\u56b2\u56b3\u56b4\u56b5\u56b6\u56b7\u56b8\u56b9\u56ba\u56bb\u56bc\u56bd\u56be\u56bf\u56c0\u56c1\u56c2\u56c3\u56c4\u56c5\u56c6\u56c7\u56c8\u56c9\u56ca\u56cb\u56cc\u56cd\u56ce\u56cf\u56d0\u56d1\u56d2\u56d3\u56d4\u56d5\u56d6\u56d7\u56d8\u56d9\u56da\u56db\u56dc\u56dd\u56de\u56df\u56e0\u56e1\u56e2\u56e3\u56e4\u56e5\u56e6\u56e7\u56e8\u56e9\u56ea\u56eb\u56ec\u56ed\u56ee\u56ef\u56f0\u56f1\u56f2\u56f3\u56f4\u56f5\u56f6\u56f7\u56f8\u56f9\u56fa\u56fb\u56fc\u56fd\u56fe\u56ff\u5700\u5701\u5702\u5703\u5704\u5705\u5706\u5707\u5708\u5709\u570a\u570b\u570c\u570d\u570e\u570f\u5710\u5711\u5712\u5713\u5714\u5715\u5716\u5717\u5718\u5719\u571a\u571b\u571c\u571d\u571e\u571f\u5720\u5721\u5722\u5723\u5724\u5725\u5726\u5727\u5728\u5729\u572a\u572b\u572c\u572d\u572e\u572f\u5730\u5731\u5732\u5733\u5734\u5735\u5736\u5737\u5738\u5739\u573a\u573b\u573c\u573d\u573e\u573f\u5740\u5741\u5742\u5743\u5744\u5745\u5746\u5747\u5748\u5749\u574a\u574b\u574c\u574d\u574e\u574f\u5750\u5751\u5752\u5753\u5754\u5755\u5756\u5757\u5758\u5759\u575a\u575b\u575c\u575d\u575e\u575f\u5760\u5761\u5762\u5763\u5764\u5765\u5766\u5767\u5768\u5769\u576a\u576b\u576c\u576d\u576e\u576f\u5770\u5771\u5772\u5773\u5774\u5775\u5776\u5777\u5778\u5779\u577a\u577b\u577c\u577d\u577e\u577f\u5780\u5781\u5782\u5783\u5784\u5785\u5786\u5787\u5788\u5789\u578a\u578b\u578c\u578d\u578e\u578f\u5790\u5791\u5792\u5793\u5794\u5795\u5796\u5797\u5798\u5799\u579a\u579b\u579c\u579d\u579e\u579f\u57a0\u57a1\u57a2\u57a3\u57a4\u57a5\u57a6\u57a7\u57a8\u57a9\u57aa\u57ab\u57ac\u57ad\u57ae\u57af\u57b0\u57b1\u57b2\u57b3\u57b4\u57b5\u57b6\u57b7\u57b8\u57b9\u57ba\u57bb\u57bc\u57bd\u57be\u57bf\u57c0\u57c1\u57c2\u57c3\u57c4\u57c5\u57c6\u57c7\u57c8\u57c9\u57ca\u57cb\u57cc\u57cd\u57ce\u57cf\u57d0\u57d1\u57d2\u57d3\u57d4\u57d5\u57d6\u57d7\u57d8\u57d9\u57da\u57db\u57dc\u57dd\u57de\u57df\u57e0\u57e1\u57e2\u57e3\u57e4\u57e5\u57e6\u57e7\u57e8\u57e9\u57ea\u57eb\u57ec\u57ed\u57ee\u57ef\u57f0\u57f1\u57f2\u57f3\u57f4\u57f5\u57f6\u57f7\u57f8\u57f9\u57fa\u57fb\u57fc\u57fd\u57fe\u57ff\u5800\u5801\u5802\u5803\u5804\u5805\u5806\u5807\u5808\u5809\u580a\u580b\u580c\u580d\u580e\u580f\u5810\u5811\u5812\u5813\u5814\u5815\u5816\u5817\u5818\u5819\u581a\u581b\u581c\u581d\u581e\u581f\u5820\u5821\u5822\u5823\u5824\u5825\u5826\u5827\u5828\u5829\u582a\u582b\u582c\u582d\u582e\u582f\u5830\u5831\u5832\u5833\u5834\u5835\u5836\u5837\u5838\u5839\u583a\u583b\u583c\u583d\u583e\u583f\u5840\u5841\u5842\u5843\u5844\u5845\u5846\u5847\u5848\u5849\u584a\u584b\u584c\u584d\u584e\u584f\u5850\u5851\u5852\u5853\u5854\u5855\u5856\u5857\u5858\u5859\u585a\u585b\u585c\u585d\u585e\u585f\u5860\u5861\u5862\u5863\u5864\u5865\u5866\u5867\u5868\u5869\u586a\u586b\u586c\u586d\u586e\u586f\u5870\u5871\u5872\u5873\u5874\u5875\u5876\u5877\u5878\u5879\u587a\u587b\u587c\u587d\u587e\u587f\u5880\u5881\u5882\u5883\u5884\u5885\u5886\u5887\u5888\u5889\u588a\u588b\u588c\u588d\u588e\u588f\u5890\u5891\u5892\u5893\u5894\u5895\u5896\u5897\u5898\u5899\u589a\u589b\u589c\u589d\u589e\u589f\u58a0\u58a1\u58a2\u58a3\u58a4\u58a5\u58a6\u58a7\u58a8\u58a9\u58aa\u58ab\u58ac\u58ad\u58ae\u58af\u58b0\u58b1\u58b2\u58b3\u58b4\u58b5\u58b6\u58b7\u58b8\u58b9\u58ba\u58bb\u58bc\u58bd\u58be\u58bf\u58c0\u58c1\u58c2\u58c3\u58c4\u58c5\u58c6\u58c7\u58c8\u58c9\u58ca\u58cb\u58cc\u58cd\u58ce\u58cf\u58d0\u58d1\u58d2\u58d3\u58d4\u58d5\u58d6\u58d7\u58d8\u58d9\u58da\u58db\u58dc\u58dd\u58de\u58df\u58e0\u58e1\u58e2\u58e3\u58e4\u58e5\u58e6\u58e7\u58e8\u58e9\u58ea\u58eb\u58ec\u58ed\u58ee\u58ef\u58f0\u58f1\u58f2\u58f3\u58f4\u58f5\u58f6\u58f7\u58f8\u58f9\u58fa\u58fb\u58fc\u58fd\u58fe\u58ff\u5900\u5901\u5902\u5903\u5904\u5905\u5906\u5907\u5908\u5909\u590a\u590b\u590c\u590d\u590e\u590f\u5910\u5911\u5912\u5913\u5914\u5915\u5916\u5917\u5918\u5919\u591a\u591b\u591c\u591d\u591e\u591f\u5920\u5921\u5922\u5923\u5924\u5925\u5926\u5927\u5928\u5929\u592a\u592b\u592c\u592d\u592e\u592f\u5930\u5931\u5932\u5933\u5934\u5935\u5936\u5937\u5938\u5939\u593a\u593b\u593c\u593d\u593e\u593f\u5940\u5941\u5942\u5943\u5944\u5945\u5946\u5947\u5948\u5949\u594a\u594b\u594c\u594d\u594e\u594f\u5950\u5951\u5952\u5953\u5954\u5955\u5956\u5957\u5958\u5959\u595a\u595b\u595c\u595d\u595e\u595f\u5960\u5961\u5962\u5963\u5964\u5965\u5966\u5967\u5968\u5969\u596a\u596b\u596c\u596d\u596e\u596f\u5970\u5971\u5972\u5973\u5974\u5975\u5976\u5977\u5978\u5979\u597a\u597b\u597c\u597d\u597e\u597f\u5980\u5981\u5982\u5983\u5984\u5985\u5986\u5987\u5988\u5989\u598a\u598b\u598c\u598d\u598e\u598f\u5990\u5991\u5992\u5993\u5994\u5995\u5996\u5997\u5998\u5999\u599a\u599b\u599c\u599d\u599e\u599f\u59a0\u59a1\u59a2\u59a3\u59a4\u59a5\u59a6\u59a7\u59a8\u59a9\u59aa\u59ab\u59ac\u59ad\u59ae\u59af\u59b0\u59b1\u59b2\u59b3\u59b4\u59b5\u59b6\u59b7\u59b8\u59b9\u59ba\u59bb\u59bc\u59bd\u59be\u59bf\u59c0\u59c1\u59c2\u59c3\u59c4\u59c5\u59c6\u59c7\u59c8\u59c9\u59ca\u59cb\u59cc\u59cd\u59ce\u59cf\u59d0\u59d1\u59d2\u59d3\u59d4\u59d5\u59d6\u59d7\u59d8\u59d9\u59da\u59db\u59dc\u59dd\u59de\u59df\u59e0\u59e1\u59e2\u59e3\u59e4\u59e5\u59e6\u59e7\u59e8\u59e9\u59ea\u59eb\u59ec\u59ed\u59ee\u59ef\u59f0\u59f1\u59f2\u59f3\u59f4\u59f5\u59f6\u59f7\u59f8\u59f9\u59fa\u59fb\u59fc\u59fd\u59fe\u59ff\u5a00\u5a01\u5a02\u5a03\u5a04\u5a05\u5a06\u5a07\u5a08\u5a09\u5a0a\u5a0b\u5a0c\u5a0d\u5a0e\u5a0f\u5a10\u5a11\u5a12\u5a13\u5a14\u5a15\u5a16\u5a17\u5a18\u5a19\u5a1a\u5a1b\u5a1c\u5a1d\u5a1e\u5a1f\u5a20\u5a21\u5a22\u5a23\u5a24\u5a25\u5a26\u5a27\u5a28\u5a29\u5a2a\u5a2b\u5a2c\u5a2d\u5a2e\u5a2f\u5a30\u5a31\u5a32\u5a33\u5a34\u5a35\u5a36\u5a37\u5a38\u5a39\u5a3a\u5a3b\u5a3c\u5a3d\u5a3e\u5a3f\u5a40\u5a41\u5a42\u5a43\u5a44\u5a45\u5a46\u5a47\u5a48\u5a49\u5a4a\u5a4b\u5a4c\u5a4d\u5a4e\u5a4f\u5a50\u5a51\u5a52\u5a53\u5a54\u5a55\u5a56\u5a57\u5a58\u5a59\u5a5a\u5a5b\u5a5c\u5a5d\u5a5e\u5a5f\u5a60\u5a61\u5a62\u5a63\u5a64\u5a65\u5a66\u5a67\u5a68\u5a69\u5a6a\u5a6b\u5a6c\u5a6d\u5a6e\u5a6f\u5a70\u5a71\u5a72\u5a73\u5a74\u5a75\u5a76\u5a77\u5a78\u5a79\u5a7a\u5a7b\u5a7c\u5a7d\u5a7e\u5a7f\u5a80\u5a81\u5a82\u5a83\u5a84\u5a85\u5a86\u5a87\u5a88\u5a89\u5a8a\u5a8b\u5a8c\u5a8d\u5a8e\u5a8f\u5a90\u5a91\u5a92\u5a93\u5a94\u5a95\u5a96\u5a97\u5a98\u5a99\u5a9a\u5a9b\u5a9c\u5a9d\u5a9e\u5a9f\u5aa0\u5aa1\u5aa2\u5aa3\u5aa4\u5aa5\u5aa6\u5aa7\u5aa8\u5aa9\u5aaa\u5aab\u5aac\u5aad\u5aae\u5aaf\u5ab0\u5ab1\u5ab2\u5ab3\u5ab4\u5ab5\u5ab6\u5ab7\u5ab8\u5ab9\u5aba\u5abb\u5abc\u5abd\u5abe\u5abf\u5ac0\u5ac1\u5ac2\u5ac3\u5ac4\u5ac5\u5ac6\u5ac7\u5ac8\u5ac9\u5aca\u5acb\u5acc\u5acd\u5ace\u5acf\u5ad0\u5ad1\u5ad2\u5ad3\u5ad4\u5ad5\u5ad6\u5ad7\u5ad8\u5ad9\u5ada\u5adb\u5adc\u5add\u5ade\u5adf\u5ae0\u5ae1\u5ae2\u5ae3\u5ae4\u5ae5\u5ae6\u5ae7\u5ae8\u5ae9\u5aea\u5aeb\u5aec\u5aed\u5aee\u5aef\u5af0\u5af1\u5af2\u5af3\u5af4\u5af5\u5af6\u5af7\u5af8\u5af9\u5afa\u5afb\u5afc\u5afd\u5afe\u5aff\u5b00\u5b01\u5b02\u5b03\u5b04\u5b05\u5b06\u5b07\u5b08\u5b09\u5b0a\u5b0b\u5b0c\u5b0d\u5b0e\u5b0f\u5b10\u5b11\u5b12\u5b13\u5b14\u5b15\u5b16\u5b17\u5b18\u5b19\u5b1a\u5b1b\u5b1c\u5b1d\u5b1e\u5b1f\u5b20\u5b21\u5b22\u5b23\u5b24\u5b25\u5b26\u5b27\u5b28\u5b29\u5b2a\u5b2b\u5b2c\u5b2d\u5b2e\u5b2f\u5b30\u5b31\u5b32\u5b33\u5b34\u5b35\u5b36\u5b37\u5b38\u5b39\u5b3a\u5b3b\u5b3c\u5b3d\u5b3e\u5b3f\u5b40\u5b41\u5b42\u5b43\u5b44\u5b45\u5b46\u5b47\u5b48\u5b49\u5b4a\u5b4b\u5b4c\u5b4d\u5b4e\u5b4f\u5b50\u5b51\u5b52\u5b53\u5b54\u5b55\u5b56\u5b57\u5b58\u5b59\u5b5a\u5b5b\u5b5c\u5b5d\u5b5e\u5b5f\u5b60\u5b61\u5b62\u5b63\u5b64\u5b65\u5b66\u5b67\u5b68\u5b69\u5b6a\u5b6b\u5b6c\u5b6d\u5b6e\u5b6f\u5b70\u5b71\u5b72\u5b73\u5b74\u5b75\u5b76\u5b77\u5b78\u5b79\u5b7a\u5b7b\u5b7c\u5b7d\u5b7e\u5b7f\u5b80\u5b81\u5b82\u5b83\u5b84\u5b85\u5b86\u5b87\u5b88\u5b89\u5b8a\u5b8b\u5b8c\u5b8d\u5b8e\u5b8f\u5b90\u5b91\u5b92\u5b93\u5b94\u5b95\u5b96\u5b97\u5b98\u5b99\u5b9a\u5b9b\u5b9c\u5b9d\u5b9e\u5b9f\u5ba0\u5ba1\u5ba2\u5ba3\u5ba4\u5ba5\u5ba6\u5ba7\u5ba8\u5ba9\u5baa\u5bab\u5bac\u5bad\u5bae\u5baf\u5bb0\u5bb1\u5bb2\u5bb3\u5bb4\u5bb5\u5bb6\u5bb7\u5bb8\u5bb9\u5bba\u5bbb\u5bbc\u5bbd\u5bbe\u5bbf\u5bc0\u5bc1\u5bc2\u5bc3\u5bc4\u5bc5\u5bc6\u5bc7\u5bc8\u5bc9\u5bca\u5bcb\u5bcc\u5bcd\u5bce\u5bcf\u5bd0\u5bd1\u5bd2\u5bd3\u5bd4\u5bd5\u5bd6\u5bd7\u5bd8\u5bd9\u5bda\u5bdb\u5bdc\u5bdd\u5bde\u5bdf\u5be0\u5be1\u5be2\u5be3\u5be4\u5be5\u5be6\u5be7\u5be8\u5be9\u5bea\u5beb\u5bec\u5bed\u5bee\u5bef\u5bf0\u5bf1\u5bf2\u5bf3\u5bf4\u5bf5\u5bf6\u5bf7\u5bf8\u5bf9\u5bfa\u5bfb\u5bfc\u5bfd\u5bfe\u5bff\u5c00\u5c01\u5c02\u5c03\u5c04\u5c05\u5c06\u5c07\u5c08\u5c09\u5c0a\u5c0b\u5c0c\u5c0d\u5c0e\u5c0f\u5c10\u5c11\u5c12\u5c13\u5c14\u5c15\u5c16\u5c17\u5c18\u5c19\u5c1a\u5c1b\u5c1c\u5c1d\u5c1e\u5c1f\u5c20\u5c21\u5c22\u5c23\u5c24\u5c25\u5c26\u5c27\u5c28\u5c29\u5c2a\u5c2b\u5c2c\u5c2d\u5c2e\u5c2f\u5c30\u5c31\u5c32\u5c33\u5c34\u5c35\u5c36\u5c37\u5c38\u5c39\u5c3a\u5c3b\u5c3c\u5c3d\u5c3e\u5c3f\u5c40\u5c41\u5c42\u5c43\u5c44\u5c45\u5c46\u5c47\u5c48\u5c49\u5c4a\u5c4b\u5c4c\u5c4d\u5c4e\u5c4f\u5c50\u5c51\u5c52\u5c53\u5c54\u5c55\u5c56\u5c57\u5c58\u5c59\u5c5a\u5c5b\u5c5c\u5c5d\u5c5e\u5c5f\u5c60\u5c61\u5c62\u5c63\u5c64\u5c65\u5c66\u5c67\u5c68\u5c69\u5c6a\u5c6b\u5c6c\u5c6d\u5c6e\u5c6f\u5c70\u5c71\u5c72\u5c73\u5c74\u5c75\u5c76\u5c77\u5c78\u5c79\u5c7a\u5c7b\u5c7c\u5c7d\u5c7e\u5c7f\u5c80\u5c81\u5c82\u5c83\u5c84\u5c85\u5c86\u5c87\u5c88\u5c89\u5c8a\u5c8b\u5c8c\u5c8d\u5c8e\u5c8f\u5c90\u5c91\u5c92\u5c93\u5c94\u5c95\u5c96\u5c97\u5c98\u5c99\u5c9a\u5c9b\u5c9c\u5c9d\u5c9e\u5c9f\u5ca0\u5ca1\u5ca2\u5ca3\u5ca4\u5ca5\u5ca6\u5ca7\u5ca8\u5ca9\u5caa\u5cab\u5cac\u5cad\u5cae\u5caf\u5cb0\u5cb1\u5cb2\u5cb3\u5cb4\u5cb5\u5cb6\u5cb7\u5cb8\u5cb9\u5cba\u5cbb\u5cbc\u5cbd\u5cbe\u5cbf\u5cc0\u5cc1\u5cc2\u5cc3\u5cc4\u5cc5\u5cc6\u5cc7\u5cc8\u5cc9\u5cca\u5ccb\u5ccc\u5ccd\u5cce\u5ccf\u5cd0\u5cd1\u5cd2\u5cd3\u5cd4\u5cd5\u5cd6\u5cd7\u5cd8\u5cd9\u5cda\u5cdb\u5cdc\u5cdd\u5cde\u5cdf\u5ce0\u5ce1\u5ce2\u5ce3\u5ce4\u5ce5\u5ce6\u5ce7\u5ce8\u5ce9\u5cea\u5ceb\u5cec\u5ced\u5cee\u5cef\u5cf0\u5cf1\u5cf2\u5cf3\u5cf4\u5cf5\u5cf6\u5cf7\u5cf8\u5cf9\u5cfa\u5cfb\u5cfc\u5cfd\u5cfe\u5cff\u5d00\u5d01\u5d02\u5d03\u5d04\u5d05\u5d06\u5d07\u5d08\u5d09\u5d0a\u5d0b\u5d0c\u5d0d\u5d0e\u5d0f\u5d10\u5d11\u5d12\u5d13\u5d14\u5d15\u5d16\u5d17\u5d18\u5d19\u5d1a\u5d1b\u5d1c\u5d1d\u5d1e\u5d1f\u5d20\u5d21\u5d22\u5d23\u5d24\u5d25\u5d26\u5d27\u5d28\u5d29\u5d2a\u5d2b\u5d2c\u5d2d\u5d2e\u5d2f\u5d30\u5d31\u5d32\u5d33\u5d34\u5d35\u5d36\u5d37\u5d38\u5d39\u5d3a\u5d3b\u5d3c\u5d3d\u5d3e\u5d3f\u5d40\u5d41\u5d42\u5d43\u5d44\u5d45\u5d46\u5d47\u5d48\u5d49\u5d4a\u5d4b\u5d4c\u5d4d\u5d4e\u5d4f\u5d50\u5d51\u5d52\u5d53\u5d54\u5d55\u5d56\u5d57\u5d58\u5d59\u5d5a\u5d5b\u5d5c\u5d5d\u5d5e\u5d5f\u5d60\u5d61\u5d62\u5d63\u5d64\u5d65\u5d66\u5d67\u5d68\u5d69\u5d6a\u5d6b\u5d6c\u5d6d\u5d6e\u5d6f\u5d70\u5d71\u5d72\u5d73\u5d74\u5d75\u5d76\u5d77\u5d78\u5d79\u5d7a\u5d7b\u5d7c\u5d7d\u5d7e\u5d7f\u5d80\u5d81\u5d82\u5d83\u5d84\u5d85\u5d86\u5d87\u5d88\u5d89\u5d8a\u5d8b\u5d8c\u5d8d\u5d8e\u5d8f\u5d90\u5d91\u5d92\u5d93\u5d94\u5d95\u5d96\u5d97\u5d98\u5d99\u5d9a\u5d9b\u5d9c\u5d9d\u5d9e\u5d9f\u5da0\u5da1\u5da2\u5da3\u5da4\u5da5\u5da6\u5da7\u5da8\u5da9\u5daa\u5dab\u5dac\u5dad\u5dae\u5daf\u5db0\u5db1\u5db2\u5db3\u5db4\u5db5\u5db6\u5db7\u5db8\u5db9\u5dba\u5dbb\u5dbc\u5dbd\u5dbe\u5dbf\u5dc0\u5dc1\u5dc2\u5dc3\u5dc4\u5dc5\u5dc6\u5dc7\u5dc8\u5dc9\u5dca\u5dcb\u5dcc\u5dcd\u5dce\u5dcf\u5dd0\u5dd1\u5dd2\u5dd3\u5dd4\u5dd5\u5dd6\u5dd7\u5dd8\u5dd9\u5dda\u5ddb\u5ddc\u5ddd\u5dde\u5ddf\u5de0\u5de1\u5de2\u5de3\u5de4\u5de5\u5de6\u5de7\u5de8\u5de9\u5dea\u5deb\u5dec\u5ded\u5dee\u5def\u5df0\u5df1\u5df2\u5df3\u5df4\u5df5\u5df6\u5df7\u5df8\u5df9\u5dfa\u5dfb\u5dfc\u5dfd\u5dfe\u5dff\u5e00\u5e01\u5e02\u5e03\u5e04\u5e05\u5e06\u5e07\u5e08\u5e09\u5e0a\u5e0b\u5e0c\u5e0d\u5e0e\u5e0f\u5e10\u5e11\u5e12\u5e13\u5e14\u5e15\u5e16\u5e17\u5e18\u5e19\u5e1a\u5e1b\u5e1c\u5e1d\u5e1e\u5e1f\u5e20\u5e21\u5e22\u5e23\u5e24\u5e25\u5e26\u5e27\u5e28\u5e29\u5e2a\u5e2b\u5e2c\u5e2d\u5e2e\u5e2f\u5e30\u5e31\u5e32\u5e33\u5e34\u5e35\u5e36\u5e37\u5e38\u5e39\u5e3a\u5e3b\u5e3c\u5e3d\u5e3e\u5e3f\u5e40\u5e41\u5e42\u5e43\u5e44\u5e45\u5e46\u5e47\u5e48\u5e49\u5e4a\u5e4b\u5e4c\u5e4d\u5e4e\u5e4f\u5e50\u5e51\u5e52\u5e53\u5e54\u5e55\u5e56\u5e57\u5e58\u5e59\u5e5a\u5e5b\u5e5c\u5e5d\u5e5e\u5e5f\u5e60\u5e61\u5e62\u5e63\u5e64\u5e65\u5e66\u5e67\u5e68\u5e69\u5e6a\u5e6b\u5e6c\u5e6d\u5e6e\u5e6f\u5e70\u5e71\u5e72\u5e73\u5e74\u5e75\u5e76\u5e77\u5e78\u5e79\u5e7a\u5e7b\u5e7c\u5e7d\u5e7e\u5e7f\u5e80\u5e81\u5e82\u5e83\u5e84\u5e85\u5e86\u5e87\u5e88\u5e89\u5e8a\u5e8b\u5e8c\u5e8d\u5e8e\u5e8f\u5e90\u5e91\u5e92\u5e93\u5e94\u5e95\u5e96\u5e97\u5e98\u5e99\u5e9a\u5e9b\u5e9c\u5e9d\u5e9e\u5e9f\u5ea0\u5ea1\u5ea2\u5ea3\u5ea4\u5ea5\u5ea6\u5ea7\u5ea8\u5ea9\u5eaa\u5eab\u5eac\u5ead\u5eae\u5eaf\u5eb0\u5eb1\u5eb2\u5eb3\u5eb4\u5eb5\u5eb6\u5eb7\u5eb8\u5eb9\u5eba\u5ebb\u5ebc\u5ebd\u5ebe\u5ebf\u5ec0\u5ec1\u5ec2\u5ec3\u5ec4\u5ec5\u5ec6\u5ec7\u5ec8\u5ec9\u5eca\u5ecb\u5ecc\u5ecd\u5ece\u5ecf\u5ed0\u5ed1\u5ed2\u5ed3\u5ed4\u5ed5\u5ed6\u5ed7\u5ed8\u5ed9\u5eda\u5edb\u5edc\u5edd\u5ede\u5edf\u5ee0\u5ee1\u5ee2\u5ee3\u5ee4\u5ee5\u5ee6\u5ee7\u5ee8\u5ee9\u5eea\u5eeb\u5eec\u5eed\u5eee\u5eef\u5ef0\u5ef1\u5ef2\u5ef3\u5ef4\u5ef5\u5ef6\u5ef7\u5ef8\u5ef9\u5efa\u5efb\u5efc\u5efd\u5efe\u5eff\u5f00\u5f01\u5f02\u5f03\u5f04\u5f05\u5f06\u5f07\u5f08\u5f09\u5f0a\u5f0b\u5f0c\u5f0d\u5f0e\u5f0f\u5f10\u5f11\u5f12\u5f13\u5f14\u5f15\u5f16\u5f17\u5f18\u5f19\u5f1a\u5f1b\u5f1c\u5f1d\u5f1e\u5f1f\u5f20\u5f21\u5f22\u5f23\u5f24\u5f25\u5f26\u5f27\u5f28\u5f29\u5f2a\u5f2b\u5f2c\u5f2d\u5f2e\u5f2f\u5f30\u5f31\u5f32\u5f33\u5f34\u5f35\u5f36\u5f37\u5f38\u5f39\u5f3a\u5f3b\u5f3c\u5f3d\u5f3e\u5f3f\u5f40\u5f41\u5f42\u5f43\u5f44\u5f45\u5f46\u5f47\u5f48\u5f49\u5f4a\u5f4b\u5f4c\u5f4d\u5f4e\u5f4f\u5f50\u5f51\u5f52\u5f53\u5f54\u5f55\u5f56\u5f57\u5f58\u5f59\u5f5a\u5f5b\u5f5c\u5f5d\u5f5e\u5f5f\u5f60\u5f61\u5f62\u5f63\u5f64\u5f65\u5f66\u5f67\u5f68\u5f69\u5f6a\u5f6b\u5f6c\u5f6d\u5f6e\u5f6f\u5f70\u5f71\u5f72\u5f73\u5f74\u5f75\u5f76\u5f77\u5f78\u5f79\u5f7a\u5f7b\u5f7c\u5f7d\u5f7e\u5f7f\u5f80\u5f81\u5f82\u5f83\u5f84\u5f85\u5f86\u5f87\u5f88\u5f89\u5f8a\u5f8b\u5f8c\u5f8d\u5f8e\u5f8f\u5f90\u5f91\u5f92\u5f93\u5f94\u5f95\u5f96\u5f97\u5f98\u5f99\u5f9a\u5f9b\u5f9c\u5f9d\u5f9e\u5f9f\u5fa0\u5fa1\u5fa2\u5fa3\u5fa4\u5fa5\u5fa6\u5fa7\u5fa8\u5fa9\u5faa\u5fab\u5fac\u5fad\u5fae\u5faf\u5fb0\u5fb1\u5fb2\u5fb3\u5fb4\u5fb5\u5fb6\u5fb7\u5fb8\u5fb9\u5fba\u5fbb\u5fbc\u5fbd\u5fbe\u5fbf\u5fc0\u5fc1\u5fc2\u5fc3\u5fc4\u5fc5\u5fc6\u5fc7\u5fc8\u5fc9\u5fca\u5fcb\u5fcc\u5fcd\u5fce\u5fcf\u5fd0\u5fd1\u5fd2\u5fd3\u5fd4\u5fd5\u5fd6\u5fd7\u5fd8\u5fd9\u5fda\u5fdb\u5fdc\u5fdd\u5fde\u5fdf\u5fe0\u5fe1\u5fe2\u5fe3\u5fe4\u5fe5\u5fe6\u5fe7\u5fe8\u5fe9\u5fea\u5feb\u5fec\u5fed\u5fee\u5fef\u5ff0\u5ff1\u5ff2\u5ff3\u5ff4\u5ff5\u5ff6\u5ff7\u5ff8\u5ff9\u5ffa\u5ffb\u5ffc\u5ffd\u5ffe\u5fff\u6000\u6001\u6002\u6003\u6004\u6005\u6006\u6007\u6008\u6009\u600a\u600b\u600c\u600d\u600e\u600f\u6010\u6011\u6012\u6013\u6014\u6015\u6016\u6017\u6018\u6019\u601a\u601b\u601c\u601d\u601e\u601f\u6020\u6021\u6022\u6023\u6024\u6025\u6026\u6027\u6028\u6029\u602a\u602b\u602c\u602d\u602e\u602f\u6030\u6031\u6032\u6033\u6034\u6035\u6036\u6037\u6038\u6039\u603a\u603b\u603c\u603d\u603e\u603f\u6040\u6041\u6042\u6043\u6044\u6045\u6046\u6047\u6048\u6049\u604a\u604b\u604c\u604d\u604e\u604f\u6050\u6051\u6052\u6053\u6054\u6055\u6056\u6057\u6058\u6059\u605a\u605b\u605c\u605d\u605e\u605f\u6060\u6061\u6062\u6063\u6064\u6065\u6066\u6067\u6068\u6069\u606a\u606b\u606c\u606d\u606e\u606f\u6070\u6071\u6072\u6073\u6074\u6075\u6076\u6077\u6078\u6079\u607a\u607b\u607c\u607d\u607e\u607f\u6080\u6081\u6082\u6083\u6084\u6085\u6086\u6087\u6088\u6089\u608a\u608b\u608c\u608d\u608e\u608f\u6090\u6091\u6092\u6093\u6094\u6095\u6096\u6097\u6098\u6099\u609a\u609b\u609c\u609d\u609e\u609f\u60a0\u60a1\u60a2\u60a3\u60a4\u60a5\u60a6\u60a7\u60a8\u60a9\u60aa\u60ab\u60ac\u60ad\u60ae\u60af\u60b0\u60b1\u60b2\u60b3\u60b4\u60b5\u60b6\u60b7\u60b8\u60b9\u60ba\u60bb\u60bc\u60bd\u60be\u60bf\u60c0\u60c1\u60c2\u60c3\u60c4\u60c5\u60c6\u60c7\u60c8\u60c9\u60ca\u60cb\u60cc\u60cd\u60ce\u60cf\u60d0\u60d1\u60d2\u60d3\u60d4\u60d5\u60d6\u60d7\u60d8\u60d9\u60da\u60db\u60dc\u60dd\u60de\u60df\u60e0\u60e1\u60e2\u60e3\u60e4\u60e5\u60e6\u60e7\u60e8\u60e9\u60ea\u60eb\u60ec\u60ed\u60ee\u60ef\u60f0\u60f1\u60f2\u60f3\u60f4\u60f5\u60f6\u60f7\u60f8\u60f9\u60fa\u60fb\u60fc\u60fd\u60fe\u60ff\u6100\u6101\u6102\u6103\u6104\u6105\u6106\u6107\u6108\u6109\u610a\u610b\u610c\u610d\u610e\u610f\u6110\u6111\u6112\u6113\u6114\u6115\u6116\u6117\u6118\u6119\u611a\u611b\u611c\u611d\u611e\u611f\u6120\u6121\u6122\u6123\u6124\u6125\u6126\u6127\u6128\u6129\u612a\u612b\u612c\u612d\u612e\u612f\u6130\u6131\u6132\u6133\u6134\u6135\u6136\u6137\u6138\u6139\u613a\u613b\u613c\u613d\u613e\u613f\u6140\u6141\u6142\u6143\u6144\u6145\u6146\u6147\u6148\u6149\u614a\u614b\u614c\u614d\u614e\u614f\u6150\u6151\u6152\u6153\u6154\u6155\u6156\u6157\u6158\u6159\u615a\u615b\u615c\u615d\u615e\u615f\u6160\u6161\u6162\u6163\u6164\u6165\u6166\u6167\u6168\u6169\u616a\u616b\u616c\u616d\u616e\u616f\u6170\u6171\u6172\u6173\u6174\u6175\u6176\u6177\u6178\u6179\u617a\u617b\u617c\u617d\u617e\u617f\u6180\u6181\u6182\u6183\u6184\u6185\u6186\u6187\u6188\u6189\u618a\u618b\u618c\u618d\u618e\u618f\u6190\u6191\u6192\u6193\u6194\u6195\u6196\u6197\u6198\u6199\u619a\u619b\u619c\u619d\u619e\u619f\u61a0\u61a1\u61a2\u61a3\u61a4\u61a5\u61a6\u61a7\u61a8\u61a9\u61aa\u61ab\u61ac\u61ad\u61ae\u61af\u61b0\u61b1\u61b2\u61b3\u61b4\u61b5\u61b6\u61b7\u61b8\u61b9\u61ba\u61bb\u61bc\u61bd\u61be\u61bf\u61c0\u61c1\u61c2\u61c3\u61c4\u61c5\u61c6\u61c7\u61c8\u61c9\u61ca\u61cb\u61cc\u61cd\u61ce\u61cf\u61d0\u61d1\u61d2\u61d3\u61d4\u61d5\u61d6\u61d7\u61d8\u61d9\u61da\u61db\u61dc\u61dd\u61de\u61df\u61e0\u61e1\u61e2\u61e3\u61e4\u61e5\u61e6\u61e7\u61e8\u61e9\u61ea\u61eb\u61ec\u61ed\u61ee\u61ef\u61f0\u61f1\u61f2\u61f3\u61f4\u61f5\u61f6\u61f7\u61f8\u61f9\u61fa\u61fb\u61fc\u61fd\u61fe\u61ff\u6200\u6201\u6202\u6203\u6204\u6205\u6206\u6207\u6208\u6209\u620a\u620b\u620c\u620d\u620e\u620f\u6210\u6211\u6212\u6213\u6214\u6215\u6216\u6217\u6218\u6219\u621a\u621b\u621c\u621d\u621e\u621f\u6220\u6221\u6222\u6223\u6224\u6225\u6226\u6227\u6228\u6229\u622a\u622b\u622c\u622d\u622e\u622f\u6230\u6231\u6232\u6233\u6234\u6235\u6236\u6237\u6238\u6239\u623a\u623b\u623c\u623d\u623e\u623f\u6240\u6241\u6242\u6243\u6244\u6245\u6246\u6247\u6248\u6249\u624a\u624b\u624c\u624d\u624e\u624f\u6250\u6251\u6252\u6253\u6254\u6255\u6256\u6257\u6258\u6259\u625a\u625b\u625c\u625d\u625e\u625f\u6260\u6261\u6262\u6263\u6264\u6265\u6266\u6267\u6268\u6269\u626a\u626b\u626c\u626d\u626e\u626f\u6270\u6271\u6272\u6273\u6274\u6275\u6276\u6277\u6278\u6279\u627a\u627b\u627c\u627d\u627e\u627f\u6280\u6281\u6282\u6283\u6284\u6285\u6286\u6287\u6288\u6289\u628a\u628b\u628c\u628d\u628e\u628f\u6290\u6291\u6292\u6293\u6294\u6295\u6296\u6297\u6298\u6299\u629a\u629b\u629c\u629d\u629e\u629f\u62a0\u62a1\u62a2\u62a3\u62a4\u62a5\u62a6\u62a7\u62a8\u62a9\u62aa\u62ab\u62ac\u62ad\u62ae\u62af\u62b0\u62b1\u62b2\u62b3\u62b4\u62b5\u62b6\u62b7\u62b8\u62b9\u62ba\u62bb\u62bc\u62bd\u62be\u62bf\u62c0\u62c1\u62c2\u62c3\u62c4\u62c5\u62c6\u62c7\u62c8\u62c9\u62ca\u62cb\u62cc\u62cd\u62ce\u62cf\u62d0\u62d1\u62d2\u62d3\u62d4\u62d5\u62d6\u62d7\u62d8\u62d9\u62da\u62db\u62dc\u62dd\u62de\u62df\u62e0\u62e1\u62e2\u62e3\u62e4\u62e5\u62e6\u62e7\u62e8\u62e9\u62ea\u62eb\u62ec\u62ed\u62ee\u62ef\u62f0\u62f1\u62f2\u62f3\u62f4\u62f5\u62f6\u62f7\u62f8\u62f9\u62fa\u62fb\u62fc\u62fd\u62fe\u62ff\u6300\u6301\u6302\u6303\u6304\u6305\u6306\u6307\u6308\u6309\u630a\u630b\u630c\u630d\u630e\u630f\u6310\u6311\u6312\u6313\u6314\u6315\u6316\u6317\u6318\u6319\u631a\u631b\u631c\u631d\u631e\u631f\u6320\u6321\u6322\u6323\u6324\u6325\u6326\u6327\u6328\u6329\u632a\u632b\u632c\u632d\u632e\u632f\u6330\u6331\u6332\u6333\u6334\u6335\u6336\u6337\u6338\u6339\u633a\u633b\u633c\u633d\u633e\u633f\u6340\u6341\u6342\u6343\u6344\u6345\u6346\u6347\u6348\u6349\u634a\u634b\u634c\u634d\u634e\u634f\u6350\u6351\u6352\u6353\u6354\u6355\u6356\u6357\u6358\u6359\u635a\u635b\u635c\u635d\u635e\u635f\u6360\u6361\u6362\u6363\u6364\u6365\u6366\u6367\u6368\u6369\u636a\u636b\u636c\u636d\u636e\u636f\u6370\u6371\u6372\u6373\u6374\u6375\u6376\u6377\u6378\u6379\u637a\u637b\u637c\u637d\u637e\u637f\u6380\u6381\u6382\u6383\u6384\u6385\u6386\u6387\u6388\u6389\u638a\u638b\u638c\u638d\u638e\u638f\u6390\u6391\u6392\u6393\u6394\u6395\u6396\u6397\u6398\u6399\u639a\u639b\u639c\u639d\u639e\u639f\u63a0\u63a1\u63a2\u63a3\u63a4\u63a5\u63a6\u63a7\u63a8\u63a9\u63aa\u63ab\u63ac\u63ad\u63ae\u63af\u63b0\u63b1\u63b2\u63b3\u63b4\u63b5\u63b6\u63b7\u63b8\u63b9\u63ba\u63bb\u63bc\u63bd\u63be\u63bf\u63c0\u63c1\u63c2\u63c3\u63c4\u63c5\u63c6\u63c7\u63c8\u63c9\u63ca\u63cb\u63cc\u63cd\u63ce\u63cf\u63d0\u63d1\u63d2\u63d3\u63d4\u63d5\u63d6\u63d7\u63d8\u63d9\u63da\u63db\u63dc\u63dd\u63de\u63df\u63e0\u63e1\u63e2\u63e3\u63e4\u63e5\u63e6\u63e7\u63e8\u63e9\u63ea\u63eb\u63ec\u63ed\u63ee\u63ef\u63f0\u63f1\u63f2\u63f3\u63f4\u63f5\u63f6\u63f7\u63f8\u63f9\u63fa\u63fb\u63fc\u63fd\u63fe\u63ff\u6400\u6401\u6402\u6403\u6404\u6405\u6406\u6407\u6408\u6409\u640a\u640b\u640c\u640d\u640e\u640f\u6410\u6411\u6412\u6413\u6414\u6415\u6416\u6417\u6418\u6419\u641a\u641b\u641c\u641d\u641e\u641f\u6420\u6421\u6422\u6423\u6424\u6425\u6426\u6427\u6428\u6429\u642a\u642b\u642c\u642d\u642e\u642f\u6430\u6431\u6432\u6433\u6434\u6435\u6436\u6437\u6438\u6439\u643a\u643b\u643c\u643d\u643e\u643f\u6440\u6441\u6442\u6443\u6444\u6445\u6446\u6447\u6448\u6449\u644a\u644b\u644c\u644d\u644e\u644f\u6450\u6451\u6452\u6453\u6454\u6455\u6456\u6457\u6458\u6459\u645a\u645b\u645c\u645d\u645e\u645f\u6460\u6461\u6462\u6463\u6464\u6465\u6466\u6467\u6468\u6469\u646a\u646b\u646c\u646d\u646e\u646f\u6470\u6471\u6472\u6473\u6474\u6475\u6476\u6477\u6478\u6479\u647a\u647b\u647c\u647d\u647e\u647f\u6480\u6481\u6482\u6483\u6484\u6485\u6486\u6487\u6488\u6489\u648a\u648b\u648c\u648d\u648e\u648f\u6490\u6491\u6492\u6493\u6494\u6495\u6496\u6497\u6498\u6499\u649a\u649b\u649c\u649d\u649e\u649f\u64a0\u64a1\u64a2\u64a3\u64a4\u64a5\u64a6\u64a7\u64a8\u64a9\u64aa\u64ab\u64ac\u64ad\u64ae\u64af\u64b0\u64b1\u64b2\u64b3\u64b4\u64b5\u64b6\u64b7\u64b8\u64b9\u64ba\u64bb\u64bc\u64bd\u64be\u64bf\u64c0\u64c1\u64c2\u64c3\u64c4\u64c5\u64c6\u64c7\u64c8\u64c9\u64ca\u64cb\u64cc\u64cd\u64ce\u64cf\u64d0\u64d1\u64d2\u64d3\u64d4\u64d5\u64d6\u64d7\u64d8\u64d9\u64da\u64db\u64dc\u64dd\u64de\u64df\u64e0\u64e1\u64e2\u64e3\u64e4\u64e5\u64e6\u64e7\u64e8\u64e9\u64ea\u64eb\u64ec\u64ed\u64ee\u64ef\u64f0\u64f1\u64f2\u64f3\u64f4\u64f5\u64f6\u64f7\u64f8\u64f9\u64fa\u64fb\u64fc\u64fd\u64fe\u64ff\u6500\u6501\u6502\u6503\u6504\u6505\u6506\u6507\u6508\u6509\u650a\u650b\u650c\u650d\u650e\u650f\u6510\u6511\u6512\u6513\u6514\u6515\u6516\u6517\u6518\u6519\u651a\u651b\u651c\u651d\u651e\u651f\u6520\u6521\u6522\u6523\u6524\u6525\u6526\u6527\u6528\u6529\u652a\u652b\u652c\u652d\u652e\u652f\u6530\u6531\u6532\u6533\u6534\u6535\u6536\u6537\u6538\u6539\u653a\u653b\u653c\u653d\u653e\u653f\u6540\u6541\u6542\u6543\u6544\u6545\u6546\u6547\u6548\u6549\u654a\u654b\u654c\u654d\u654e\u654f\u6550\u6551\u6552\u6553\u6554\u6555\u6556\u6557\u6558\u6559\u655a\u655b\u655c\u655d\u655e\u655f\u6560\u6561\u6562\u6563\u6564\u6565\u6566\u6567\u6568\u6569\u656a\u656b\u656c\u656d\u656e\u656f\u6570\u6571\u6572\u6573\u6574\u6575\u6576\u6577\u6578\u6579\u657a\u657b\u657c\u657d\u657e\u657f\u6580\u6581\u6582\u6583\u6584\u6585\u6586\u6587\u6588\u6589\u658a\u658b\u658c\u658d\u658e\u658f\u6590\u6591\u6592\u6593\u6594\u6595\u6596\u6597\u6598\u6599\u659a\u659b\u659c\u659d\u659e\u659f\u65a0\u65a1\u65a2\u65a3\u65a4\u65a5\u65a6\u65a7\u65a8\u65a9\u65aa\u65ab\u65ac\u65ad\u65ae\u65af\u65b0\u65b1\u65b2\u65b3\u65b4\u65b5\u65b6\u65b7\u65b8\u65b9\u65ba\u65bb\u65bc\u65bd\u65be\u65bf\u65c0\u65c1\u65c2\u65c3\u65c4\u65c5\u65c6\u65c7\u65c8\u65c9\u65ca\u65cb\u65cc\u65cd\u65ce\u65cf\u65d0\u65d1\u65d2\u65d3\u65d4\u65d5\u65d6\u65d7\u65d8\u65d9\u65da\u65db\u65dc\u65dd\u65de\u65df\u65e0\u65e1\u65e2\u65e3\u65e4\u65e5\u65e6\u65e7\u65e8\u65e9\u65ea\u65eb\u65ec\u65ed\u65ee\u65ef\u65f0\u65f1\u65f2\u65f3\u65f4\u65f5\u65f6\u65f7\u65f8\u65f9\u65fa\u65fb\u65fc\u65fd\u65fe\u65ff\u6600\u6601\u6602\u6603\u6604\u6605\u6606\u6607\u6608\u6609\u660a\u660b\u660c\u660d\u660e\u660f\u6610\u6611\u6612\u6613\u6614\u6615\u6616\u6617\u6618\u6619\u661a\u661b\u661c\u661d\u661e\u661f\u6620\u6621\u6622\u6623\u6624\u6625\u6626\u6627\u6628\u6629\u662a\u662b\u662c\u662d\u662e\u662f\u6630\u6631\u6632\u6633\u6634\u6635\u6636\u6637\u6638\u6639\u663a\u663b\u663c\u663d\u663e\u663f\u6640\u6641\u6642\u6643\u6644\u6645\u6646\u6647\u6648\u6649\u664a\u664b\u664c\u664d\u664e\u664f\u6650\u6651\u6652\u6653\u6654\u6655\u6656\u6657\u6658\u6659\u665a\u665b\u665c\u665d\u665e\u665f\u6660\u6661\u6662\u6663\u6664\u6665\u6666\u6667\u6668\u6669\u666a\u666b\u666c\u666d\u666e\u666f\u6670\u6671\u6672\u6673\u6674\u6675\u6676\u6677\u6678\u6679\u667a\u667b\u667c\u667d\u667e\u667f\u6680\u6681\u6682\u6683\u6684\u6685\u6686\u6687\u6688\u6689\u668a\u668b\u668c\u668d\u668e\u668f\u6690\u6691\u6692\u6693\u6694\u6695\u6696\u6697\u6698\u6699\u669a\u669b\u669c\u669d\u669e\u669f\u66a0\u66a1\u66a2\u66a3\u66a4\u66a5\u66a6\u66a7\u66a8\u66a9\u66aa\u66ab\u66ac\u66ad\u66ae\u66af\u66b0\u66b1\u66b2\u66b3\u66b4\u66b5\u66b6\u66b7\u66b8\u66b9\u66ba\u66bb\u66bc\u66bd\u66be\u66bf\u66c0\u66c1\u66c2\u66c3\u66c4\u66c5\u66c6\u66c7\u66c8\u66c9\u66ca\u66cb\u66cc\u66cd\u66ce\u66cf\u66d0\u66d1\u66d2\u66d3\u66d4\u66d5\u66d6\u66d7\u66d8\u66d9\u66da\u66db\u66dc\u66dd\u66de\u66df\u66e0\u66e1\u66e2\u66e3\u66e4\u66e5\u66e6\u66e7\u66e8\u66e9\u66ea\u66eb\u66ec\u66ed\u66ee\u66ef\u66f0\u66f1\u66f2\u66f3\u66f4\u66f5\u66f6\u66f7\u66f8\u66f9\u66fa\u66fb\u66fc\u66fd\u66fe\u66ff\u6700\u6701\u6702\u6703\u6704\u6705\u6706\u6707\u6708\u6709\u670a\u670b\u670c\u670d\u670e\u670f\u6710\u6711\u6712\u6713\u6714\u6715\u6716\u6717\u6718\u6719\u671a\u671b\u671c\u671d\u671e\u671f\u6720\u6721\u6722\u6723\u6724\u6725\u6726\u6727\u6728\u6729\u672a\u672b\u672c\u672d\u672e\u672f\u6730\u6731\u6732\u6733\u6734\u6735\u6736\u6737\u6738\u6739\u673a\u673b\u673c\u673d\u673e\u673f\u6740\u6741\u6742\u6743\u6744\u6745\u6746\u6747\u6748\u6749\u674a\u674b\u674c\u674d\u674e\u674f\u6750\u6751\u6752\u6753\u6754\u6755\u6756\u6757\u6758\u6759\u675a\u675b\u675c\u675d\u675e\u675f\u6760\u6761\u6762\u6763\u6764\u6765\u6766\u6767\u6768\u6769\u676a\u676b\u676c\u676d\u676e\u676f\u6770\u6771\u6772\u6773\u6774\u6775\u6776\u6777\u6778\u6779\u677a\u677b\u677c\u677d\u677e\u677f\u6780\u6781\u6782\u6783\u6784\u6785\u6786\u6787\u6788\u6789\u678a\u678b\u678c\u678d\u678e\u678f\u6790\u6791\u6792\u6793\u6794\u6795\u6796\u6797\u6798\u6799\u679a\u679b\u679c\u679d\u679e\u679f\u67a0\u67a1\u67a2\u67a3\u67a4\u67a5\u67a6\u67a7\u67a8\u67a9\u67aa\u67ab\u67ac\u67ad\u67ae\u67af\u67b0\u67b1\u67b2\u67b3\u67b4\u67b5\u67b6\u67b7\u67b8\u67b9\u67ba\u67bb\u67bc\u67bd\u67be\u67bf\u67c0\u67c1\u67c2\u67c3\u67c4\u67c5\u67c6\u67c7\u67c8\u67c9\u67ca\u67cb\u67cc\u67cd\u67ce\u67cf\u67d0\u67d1\u67d2\u67d3\u67d4\u67d5\u67d6\u67d7\u67d8\u67d9\u67da\u67db\u67dc\u67dd\u67de\u67df\u67e0\u67e1\u67e2\u67e3\u67e4\u67e5\u67e6\u67e7\u67e8\u67e9\u67ea\u67eb\u67ec\u67ed\u67ee\u67ef\u67f0\u67f1\u67f2\u67f3\u67f4\u67f5\u67f6\u67f7\u67f8\u67f9\u67fa\u67fb\u67fc\u67fd\u67fe\u67ff\u6800\u6801\u6802\u6803\u6804\u6805\u6806\u6807\u6808\u6809\u680a\u680b\u680c\u680d\u680e\u680f\u6810\u6811\u6812\u6813\u6814\u6815\u6816\u6817\u6818\u6819\u681a\u681b\u681c\u681d\u681e\u681f\u6820\u6821\u6822\u6823\u6824\u6825\u6826\u6827\u6828\u6829\u682a\u682b\u682c\u682d\u682e\u682f\u6830\u6831\u6832\u6833\u6834\u6835\u6836\u6837\u6838\u6839\u683a\u683b\u683c\u683d\u683e\u683f\u6840\u6841\u6842\u6843\u6844\u6845\u6846\u6847\u6848\u6849\u684a\u684b\u684c\u684d\u684e\u684f\u6850\u6851\u6852\u6853\u6854\u6855\u6856\u6857\u6858\u6859\u685a\u685b\u685c\u685d\u685e\u685f\u6860\u6861\u6862\u6863\u6864\u6865\u6866\u6867\u6868\u6869\u686a\u686b\u686c\u686d\u686e\u686f\u6870\u6871\u6872\u6873\u6874\u6875\u6876\u6877\u6878\u6879\u687a\u687b\u687c\u687d\u687e\u687f\u6880\u6881\u6882\u6883\u6884\u6885\u6886\u6887\u6888\u6889\u688a\u688b\u688c\u688d\u688e\u688f\u6890\u6891\u6892\u6893\u6894\u6895\u6896\u6897\u6898\u6899\u689a\u689b\u689c\u689d\u689e\u689f\u68a0\u68a1\u68a2\u68a3\u68a4\u68a5\u68a6\u68a7\u68a8\u68a9\u68aa\u68ab\u68ac\u68ad\u68ae\u68af\u68b0\u68b1\u68b2\u68b3\u68b4\u68b5\u68b6\u68b7\u68b8\u68b9\u68ba\u68bb\u68bc\u68bd\u68be\u68bf\u68c0\u68c1\u68c2\u68c3\u68c4\u68c5\u68c6\u68c7\u68c8\u68c9\u68ca\u68cb\u68cc\u68cd\u68ce\u68cf\u68d0\u68d1\u68d2\u68d3\u68d4\u68d5\u68d6\u68d7\u68d8\u68d9\u68da\u68db\u68dc\u68dd\u68de\u68df\u68e0\u68e1\u68e2\u68e3\u68e4\u68e5\u68e6\u68e7\u68e8\u68e9\u68ea\u68eb\u68ec\u68ed\u68ee\u68ef\u68f0\u68f1\u68f2\u68f3\u68f4\u68f5\u68f6\u68f7\u68f8\u68f9\u68fa\u68fb\u68fc\u68fd\u68fe\u68ff\u6900\u6901\u6902\u6903\u6904\u6905\u6906\u6907\u6908\u6909\u690a\u690b\u690c\u690d\u690e\u690f\u6910\u6911\u6912\u6913\u6914\u6915\u6916\u6917\u6918\u6919\u691a\u691b\u691c\u691d\u691e\u691f\u6920\u6921\u6922\u6923\u6924\u6925\u6926\u6927\u6928\u6929\u692a\u692b\u692c\u692d\u692e\u692f\u6930\u6931\u6932\u6933\u6934\u6935\u6936\u6937\u6938\u6939\u693a\u693b\u693c\u693d\u693e\u693f\u6940\u6941\u6942\u6943\u6944\u6945\u6946\u6947\u6948\u6949\u694a\u694b\u694c\u694d\u694e\u694f\u6950\u6951\u6952\u6953\u6954\u6955\u6956\u6957\u6958\u6959\u695a\u695b\u695c\u695d\u695e\u695f\u6960\u6961\u6962\u6963\u6964\u6965\u6966\u6967\u6968\u6969\u696a\u696b\u696c\u696d\u696e\u696f\u6970\u6971\u6972\u6973\u6974\u6975\u6976\u6977\u6978\u6979\u697a\u697b\u697c\u697d\u697e\u697f\u6980\u6981\u6982\u6983\u6984\u6985\u6986\u6987\u6988\u6989\u698a\u698b\u698c\u698d\u698e\u698f\u6990\u6991\u6992\u6993\u6994\u6995\u6996\u6997\u6998\u6999\u699a\u699b\u699c\u699d\u699e\u699f\u69a0\u69a1\u69a2\u69a3\u69a4\u69a5\u69a6\u69a7\u69a8\u69a9\u69aa\u69ab\u69ac\u69ad\u69ae\u69af\u69b0\u69b1\u69b2\u69b3\u69b4\u69b5\u69b6\u69b7\u69b8\u69b9\u69ba\u69bb\u69bc\u69bd\u69be\u69bf\u69c0\u69c1\u69c2\u69c3\u69c4\u69c5\u69c6\u69c7\u69c8\u69c9\u69ca\u69cb\u69cc\u69cd\u69ce\u69cf\u69d0\u69d1\u69d2\u69d3\u69d4\u69d5\u69d6\u69d7\u69d8\u69d9\u69da\u69db\u69dc\u69dd\u69de\u69df\u69e0\u69e1\u69e2\u69e3\u69e4\u69e5\u69e6\u69e7\u69e8\u69e9\u69ea\u69eb\u69ec\u69ed\u69ee\u69ef\u69f0\u69f1\u69f2\u69f3\u69f4\u69f5\u69f6\u69f7\u69f8\u69f9\u69fa\u69fb\u69fc\u69fd\u69fe\u69ff\u6a00\u6a01\u6a02\u6a03\u6a04\u6a05\u6a06\u6a07\u6a08\u6a09\u6a0a\u6a0b\u6a0c\u6a0d\u6a0e\u6a0f\u6a10\u6a11\u6a12\u6a13\u6a14\u6a15\u6a16\u6a17\u6a18\u6a19\u6a1a\u6a1b\u6a1c\u6a1d\u6a1e\u6a1f\u6a20\u6a21\u6a22\u6a23\u6a24\u6a25\u6a26\u6a27\u6a28\u6a29\u6a2a\u6a2b\u6a2c\u6a2d\u6a2e\u6a2f\u6a30\u6a31\u6a32\u6a33\u6a34\u6a35\u6a36\u6a37\u6a38\u6a39\u6a3a\u6a3b\u6a3c\u6a3d\u6a3e\u6a3f\u6a40\u6a41\u6a42\u6a43\u6a44\u6a45\u6a46\u6a47\u6a48\u6a49\u6a4a\u6a4b\u6a4c\u6a4d\u6a4e\u6a4f\u6a50\u6a51\u6a52\u6a53\u6a54\u6a55\u6a56\u6a57\u6a58\u6a59\u6a5a\u6a5b\u6a5c\u6a5d\u6a5e\u6a5f\u6a60\u6a61\u6a62\u6a63\u6a64\u6a65\u6a66\u6a67\u6a68\u6a69\u6a6a\u6a6b\u6a6c\u6a6d\u6a6e\u6a6f\u6a70\u6a71\u6a72\u6a73\u6a74\u6a75\u6a76\u6a77\u6a78\u6a79\u6a7a\u6a7b\u6a7c\u6a7d\u6a7e\u6a7f\u6a80\u6a81\u6a82\u6a83\u6a84\u6a85\u6a86\u6a87\u6a88\u6a89\u6a8a\u6a8b\u6a8c\u6a8d\u6a8e\u6a8f\u6a90\u6a91\u6a92\u6a93\u6a94\u6a95\u6a96\u6a97\u6a98\u6a99\u6a9a\u6a9b\u6a9c\u6a9d\u6a9e\u6a9f\u6aa0\u6aa1\u6aa2\u6aa3\u6aa4\u6aa5\u6aa6\u6aa7\u6aa8\u6aa9\u6aaa\u6aab\u6aac\u6aad\u6aae\u6aaf\u6ab0\u6ab1\u6ab2\u6ab3\u6ab4\u6ab5\u6ab6\u6ab7\u6ab8\u6ab9\u6aba\u6abb\u6abc\u6abd\u6abe\u6abf\u6ac0\u6ac1\u6ac2\u6ac3\u6ac4\u6ac5\u6ac6\u6ac7\u6ac8\u6ac9\u6aca\u6acb\u6acc\u6acd\u6ace\u6acf\u6ad0\u6ad1\u6ad2\u6ad3\u6ad4\u6ad5\u6ad6\u6ad7\u6ad8\u6ad9\u6ada\u6adb\u6adc\u6add\u6ade\u6adf\u6ae0\u6ae1\u6ae2\u6ae3\u6ae4\u6ae5\u6ae6\u6ae7\u6ae8\u6ae9\u6aea\u6aeb\u6aec\u6aed\u6aee\u6aef\u6af0\u6af1\u6af2\u6af3\u6af4\u6af5\u6af6\u6af7\u6af8\u6af9\u6afa\u6afb\u6afc\u6afd\u6afe\u6aff\u6b00\u6b01\u6b02\u6b03\u6b04\u6b05\u6b06\u6b07\u6b08\u6b09\u6b0a\u6b0b\u6b0c\u6b0d\u6b0e\u6b0f\u6b10\u6b11\u6b12\u6b13\u6b14\u6b15\u6b16\u6b17\u6b18\u6b19\u6b1a\u6b1b\u6b1c\u6b1d\u6b1e\u6b1f\u6b20\u6b21\u6b22\u6b23\u6b24\u6b25\u6b26\u6b27\u6b28\u6b29\u6b2a\u6b2b\u6b2c\u6b2d\u6b2e\u6b2f\u6b30\u6b31\u6b32\u6b33\u6b34\u6b35\u6b36\u6b37\u6b38\u6b39\u6b3a\u6b3b\u6b3c\u6b3d\u6b3e\u6b3f\u6b40\u6b41\u6b42\u6b43\u6b44\u6b45\u6b46\u6b47\u6b48\u6b49\u6b4a\u6b4b\u6b4c\u6b4d\u6b4e\u6b4f\u6b50\u6b51\u6b52\u6b53\u6b54\u6b55\u6b56\u6b57\u6b58\u6b59\u6b5a\u6b5b\u6b5c\u6b5d\u6b5e\u6b5f\u6b60\u6b61\u6b62\u6b63\u6b64\u6b65\u6b66\u6b67\u6b68\u6b69\u6b6a\u6b6b\u6b6c\u6b6d\u6b6e\u6b6f\u6b70\u6b71\u6b72\u6b73\u6b74\u6b75\u6b76\u6b77\u6b78\u6b79\u6b7a\u6b7b\u6b7c\u6b7d\u6b7e\u6b7f\u6b80\u6b81\u6b82\u6b83\u6b84\u6b85\u6b86\u6b87\u6b88\u6b89\u6b8a\u6b8b\u6b8c\u6b8d\u6b8e\u6b8f\u6b90\u6b91\u6b92\u6b93\u6b94\u6b95\u6b96\u6b97\u6b98\u6b99\u6b9a\u6b9b\u6b9c\u6b9d\u6b9e\u6b9f\u6ba0\u6ba1\u6ba2\u6ba3\u6ba4\u6ba5\u6ba6\u6ba7\u6ba8\u6ba9\u6baa\u6bab\u6bac\u6bad\u6bae\u6baf\u6bb0\u6bb1\u6bb2\u6bb3\u6bb4\u6bb5\u6bb6\u6bb7\u6bb8\u6bb9\u6bba\u6bbb\u6bbc\u6bbd\u6bbe\u6bbf\u6bc0\u6bc1\u6bc2\u6bc3\u6bc4\u6bc5\u6bc6\u6bc7\u6bc8\u6bc9\u6bca\u6bcb\u6bcc\u6bcd\u6bce\u6bcf\u6bd0\u6bd1\u6bd2\u6bd3\u6bd4\u6bd5\u6bd6\u6bd7\u6bd8\u6bd9\u6bda\u6bdb\u6bdc\u6bdd\u6bde\u6bdf\u6be0\u6be1\u6be2\u6be3\u6be4\u6be5\u6be6\u6be7\u6be8\u6be9\u6bea\u6beb\u6bec\u6bed\u6bee\u6bef\u6bf0\u6bf1\u6bf2\u6bf3\u6bf4\u6bf5\u6bf6\u6bf7\u6bf8\u6bf9\u6bfa\u6bfb\u6bfc\u6bfd\u6bfe\u6bff\u6c00\u6c01\u6c02\u6c03\u6c04\u6c05\u6c06\u6c07\u6c08\u6c09\u6c0a\u6c0b\u6c0c\u6c0d\u6c0e\u6c0f\u6c10\u6c11\u6c12\u6c13\u6c14\u6c15\u6c16\u6c17\u6c18\u6c19\u6c1a\u6c1b\u6c1c\u6c1d\u6c1e\u6c1f\u6c20\u6c21\u6c22\u6c23\u6c24\u6c25\u6c26\u6c27\u6c28\u6c29\u6c2a\u6c2b\u6c2c\u6c2d\u6c2e\u6c2f\u6c30\u6c31\u6c32\u6c33\u6c34\u6c35\u6c36\u6c37\u6c38\u6c39\u6c3a\u6c3b\u6c3c\u6c3d\u6c3e\u6c3f\u6c40\u6c41\u6c42\u6c43\u6c44\u6c45\u6c46\u6c47\u6c48\u6c49\u6c4a\u6c4b\u6c4c\u6c4d\u6c4e\u6c4f\u6c50\u6c51\u6c52\u6c53\u6c54\u6c55\u6c56\u6c57\u6c58\u6c59\u6c5a\u6c5b\u6c5c\u6c5d\u6c5e\u6c5f\u6c60\u6c61\u6c62\u6c63\u6c64\u6c65\u6c66\u6c67\u6c68\u6c69\u6c6a\u6c6b\u6c6c\u6c6d\u6c6e\u6c6f\u6c70\u6c71\u6c72\u6c73\u6c74\u6c75\u6c76\u6c77\u6c78\u6c79\u6c7a\u6c7b\u6c7c\u6c7d\u6c7e\u6c7f\u6c80\u6c81\u6c82\u6c83\u6c84\u6c85\u6c86\u6c87\u6c88\u6c89\u6c8a\u6c8b\u6c8c\u6c8d\u6c8e\u6c8f\u6c90\u6c91\u6c92\u6c93\u6c94\u6c95\u6c96\u6c97\u6c98\u6c99\u6c9a\u6c9b\u6c9c\u6c9d\u6c9e\u6c9f\u6ca0\u6ca1\u6ca2\u6ca3\u6ca4\u6ca5\u6ca6\u6ca7\u6ca8\u6ca9\u6caa\u6cab\u6cac\u6cad\u6cae\u6caf\u6cb0\u6cb1\u6cb2\u6cb3\u6cb4\u6cb5\u6cb6\u6cb7\u6cb8\u6cb9\u6cba\u6cbb\u6cbc\u6cbd\u6cbe\u6cbf\u6cc0\u6cc1\u6cc2\u6cc3\u6cc4\u6cc5\u6cc6\u6cc7\u6cc8\u6cc9\u6cca\u6ccb\u6ccc\u6ccd\u6cce\u6ccf\u6cd0\u6cd1\u6cd2\u6cd3\u6cd4\u6cd5\u6cd6\u6cd7\u6cd8\u6cd9\u6cda\u6cdb\u6cdc\u6cdd\u6cde\u6cdf\u6ce0\u6ce1\u6ce2\u6ce3\u6ce4\u6ce5\u6ce6\u6ce7\u6ce8\u6ce9\u6cea\u6ceb\u6cec\u6ced\u6cee\u6cef\u6cf0\u6cf1\u6cf2\u6cf3\u6cf4\u6cf5\u6cf6\u6cf7\u6cf8\u6cf9\u6cfa\u6cfb\u6cfc\u6cfd\u6cfe\u6cff\u6d00\u6d01\u6d02\u6d03\u6d04\u6d05\u6d06\u6d07\u6d08\u6d09\u6d0a\u6d0b\u6d0c\u6d0d\u6d0e\u6d0f\u6d10\u6d11\u6d12\u6d13\u6d14\u6d15\u6d16\u6d17\u6d18\u6d19\u6d1a\u6d1b\u6d1c\u6d1d\u6d1e\u6d1f\u6d20\u6d21\u6d22\u6d23\u6d24\u6d25\u6d26\u6d27\u6d28\u6d29\u6d2a\u6d2b\u6d2c\u6d2d\u6d2e\u6d2f\u6d30\u6d31\u6d32\u6d33\u6d34\u6d35\u6d36\u6d37\u6d38\u6d39\u6d3a\u6d3b\u6d3c\u6d3d\u6d3e\u6d3f\u6d40\u6d41\u6d42\u6d43\u6d44\u6d45\u6d46\u6d47\u6d48\u6d49\u6d4a\u6d4b\u6d4c\u6d4d\u6d4e\u6d4f\u6d50\u6d51\u6d52\u6d53\u6d54\u6d55\u6d56\u6d57\u6d58\u6d59\u6d5a\u6d5b\u6d5c\u6d5d\u6d5e\u6d5f\u6d60\u6d61\u6d62\u6d63\u6d64\u6d65\u6d66\u6d67\u6d68\u6d69\u6d6a\u6d6b\u6d6c\u6d6d\u6d6e\u6d6f\u6d70\u6d71\u6d72\u6d73\u6d74\u6d75\u6d76\u6d77\u6d78\u6d79\u6d7a\u6d7b\u6d7c\u6d7d\u6d7e\u6d7f\u6d80\u6d81\u6d82\u6d83\u6d84\u6d85\u6d86\u6d87\u6d88\u6d89\u6d8a\u6d8b\u6d8c\u6d8d\u6d8e\u6d8f\u6d90\u6d91\u6d92\u6d93\u6d94\u6d95\u6d96\u6d97\u6d98\u6d99\u6d9a\u6d9b\u6d9c\u6d9d\u6d9e\u6d9f\u6da0\u6da1\u6da2\u6da3\u6da4\u6da5\u6da6\u6da7\u6da8\u6da9\u6daa\u6dab\u6dac\u6dad\u6dae\u6daf\u6db0\u6db1\u6db2\u6db3\u6db4\u6db5\u6db6\u6db7\u6db8\u6db9\u6dba\u6dbb\u6dbc\u6dbd\u6dbe\u6dbf\u6dc0\u6dc1\u6dc2\u6dc3\u6dc4\u6dc5\u6dc6\u6dc7\u6dc8\u6dc9\u6dca\u6dcb\u6dcc\u6dcd\u6dce\u6dcf\u6dd0\u6dd1\u6dd2\u6dd3\u6dd4\u6dd5\u6dd6\u6dd7\u6dd8\u6dd9\u6dda\u6ddb\u6ddc\u6ddd\u6dde\u6ddf\u6de0\u6de1\u6de2\u6de3\u6de4\u6de5\u6de6\u6de7\u6de8\u6de9\u6dea\u6deb\u6dec\u6ded\u6dee\u6def\u6df0\u6df1\u6df2\u6df3\u6df4\u6df5\u6df6\u6df7\u6df8\u6df9\u6dfa\u6dfb\u6dfc\u6dfd\u6dfe\u6dff\u6e00\u6e01\u6e02\u6e03\u6e04\u6e05\u6e06\u6e07\u6e08\u6e09\u6e0a\u6e0b\u6e0c\u6e0d\u6e0e\u6e0f\u6e10\u6e11\u6e12\u6e13\u6e14\u6e15\u6e16\u6e17\u6e18\u6e19\u6e1a\u6e1b\u6e1c\u6e1d\u6e1e\u6e1f\u6e20\u6e21\u6e22\u6e23\u6e24\u6e25\u6e26\u6e27\u6e28\u6e29\u6e2a\u6e2b\u6e2c\u6e2d\u6e2e\u6e2f\u6e30\u6e31\u6e32\u6e33\u6e34\u6e35\u6e36\u6e37\u6e38\u6e39\u6e3a\u6e3b\u6e3c\u6e3d\u6e3e\u6e3f\u6e40\u6e41\u6e42\u6e43\u6e44\u6e45\u6e46\u6e47\u6e48\u6e49\u6e4a\u6e4b\u6e4c\u6e4d\u6e4e\u6e4f\u6e50\u6e51\u6e52\u6e53\u6e54\u6e55\u6e56\u6e57\u6e58\u6e59\u6e5a\u6e5b\u6e5c\u6e5d\u6e5e\u6e5f\u6e60\u6e61\u6e62\u6e63\u6e64\u6e65\u6e66\u6e67\u6e68\u6e69\u6e6a\u6e6b\u6e6c\u6e6d\u6e6e\u6e6f\u6e70\u6e71\u6e72\u6e73\u6e74\u6e75\u6e76\u6e77\u6e78\u6e79\u6e7a\u6e7b\u6e7c\u6e7d\u6e7e\u6e7f\u6e80\u6e81\u6e82\u6e83\u6e84\u6e85\u6e86\u6e87\u6e88\u6e89\u6e8a\u6e8b\u6e8c\u6e8d\u6e8e\u6e8f\u6e90\u6e91\u6e92\u6e93\u6e94\u6e95\u6e96\u6e97\u6e98\u6e99\u6e9a\u6e9b\u6e9c\u6e9d\u6e9e\u6e9f\u6ea0\u6ea1\u6ea2\u6ea3\u6ea4\u6ea5\u6ea6\u6ea7\u6ea8\u6ea9\u6eaa\u6eab\u6eac\u6ead\u6eae\u6eaf\u6eb0\u6eb1\u6eb2\u6eb3\u6eb4\u6eb5\u6eb6\u6eb7\u6eb8\u6eb9\u6eba\u6ebb\u6ebc\u6ebd\u6ebe\u6ebf\u6ec0\u6ec1\u6ec2\u6ec3\u6ec4\u6ec5\u6ec6\u6ec7\u6ec8\u6ec9\u6eca\u6ecb\u6ecc\u6ecd\u6ece\u6ecf\u6ed0\u6ed1\u6ed2\u6ed3\u6ed4\u6ed5\u6ed6\u6ed7\u6ed8\u6ed9\u6eda\u6edb\u6edc\u6edd\u6ede\u6edf\u6ee0\u6ee1\u6ee2\u6ee3\u6ee4\u6ee5\u6ee6\u6ee7\u6ee8\u6ee9\u6eea\u6eeb\u6eec\u6eed\u6eee\u6eef\u6ef0\u6ef1\u6ef2\u6ef3\u6ef4\u6ef5\u6ef6\u6ef7\u6ef8\u6ef9\u6efa\u6efb\u6efc\u6efd\u6efe\u6eff\u6f00\u6f01\u6f02\u6f03\u6f04\u6f05\u6f06\u6f07\u6f08\u6f09\u6f0a\u6f0b\u6f0c\u6f0d\u6f0e\u6f0f\u6f10\u6f11\u6f12\u6f13\u6f14\u6f15\u6f16\u6f17\u6f18\u6f19\u6f1a\u6f1b\u6f1c\u6f1d\u6f1e\u6f1f\u6f20\u6f21\u6f22\u6f23\u6f24\u6f25\u6f26\u6f27\u6f28\u6f29\u6f2a\u6f2b\u6f2c\u6f2d\u6f2e\u6f2f\u6f30\u6f31\u6f32\u6f33\u6f34\u6f35\u6f36\u6f37\u6f38\u6f39\u6f3a\u6f3b\u6f3c\u6f3d\u6f3e\u6f3f\u6f40\u6f41\u6f42\u6f43\u6f44\u6f45\u6f46\u6f47\u6f48\u6f49\u6f4a\u6f4b\u6f4c\u6f4d\u6f4e\u6f4f\u6f50\u6f51\u6f52\u6f53\u6f54\u6f55\u6f56\u6f57\u6f58\u6f59\u6f5a\u6f5b\u6f5c\u6f5d\u6f5e\u6f5f\u6f60\u6f61\u6f62\u6f63\u6f64\u6f65\u6f66\u6f67\u6f68\u6f69\u6f6a\u6f6b\u6f6c\u6f6d\u6f6e\u6f6f\u6f70\u6f71\u6f72\u6f73\u6f74\u6f75\u6f76\u6f77\u6f78\u6f79\u6f7a\u6f7b\u6f7c\u6f7d\u6f7e\u6f7f\u6f80\u6f81\u6f82\u6f83\u6f84\u6f85\u6f86\u6f87\u6f88\u6f89\u6f8a\u6f8b\u6f8c\u6f8d\u6f8e\u6f8f\u6f90\u6f91\u6f92\u6f93\u6f94\u6f95\u6f96\u6f97\u6f98\u6f99\u6f9a\u6f9b\u6f9c\u6f9d\u6f9e\u6f9f\u6fa0\u6fa1\u6fa2\u6fa3\u6fa4\u6fa5\u6fa6\u6fa7\u6fa8\u6fa9\u6faa\u6fab\u6fac\u6fad\u6fae\u6faf\u6fb0\u6fb1\u6fb2\u6fb3\u6fb4\u6fb5\u6fb6\u6fb7\u6fb8\u6fb9\u6fba\u6fbb\u6fbc\u6fbd\u6fbe\u6fbf\u6fc0\u6fc1\u6fc2\u6fc3\u6fc4\u6fc5\u6fc6\u6fc7\u6fc8\u6fc9\u6fca\u6fcb\u6fcc\u6fcd\u6fce\u6fcf\u6fd0\u6fd1\u6fd2\u6fd3\u6fd4\u6fd5\u6fd6\u6fd7\u6fd8\u6fd9\u6fda\u6fdb\u6fdc\u6fdd\u6fde\u6fdf\u6fe0\u6fe1\u6fe2\u6fe3\u6fe4\u6fe5\u6fe6\u6fe7\u6fe8\u6fe9\u6fea\u6feb\u6fec\u6fed\u6fee\u6fef\u6ff0\u6ff1\u6ff2\u6ff3\u6ff4\u6ff5\u6ff6\u6ff7\u6ff8\u6ff9\u6ffa\u6ffb\u6ffc\u6ffd\u6ffe\u6fff\u7000\u7001\u7002\u7003\u7004\u7005\u7006\u7007\u7008\u7009\u700a\u700b\u700c\u700d\u700e\u700f\u7010\u7011\u7012\u7013\u7014\u7015\u7016\u7017\u7018\u7019\u701a\u701b\u701c\u701d\u701e\u701f\u7020\u7021\u7022\u7023\u7024\u7025\u7026\u7027\u7028\u7029\u702a\u702b\u702c\u702d\u702e\u702f\u7030\u7031\u7032\u7033\u7034\u7035\u7036\u7037\u7038\u7039\u703a\u703b\u703c\u703d\u703e\u703f\u7040\u7041\u7042\u7043\u7044\u7045\u7046\u7047\u7048\u7049\u704a\u704b\u704c\u704d\u704e\u704f\u7050\u7051\u7052\u7053\u7054\u7055\u7056\u7057\u7058\u7059\u705a\u705b\u705c\u705d\u705e\u705f\u7060\u7061\u7062\u7063\u7064\u7065\u7066\u7067\u7068\u7069\u706a\u706b\u706c\u706d\u706e\u706f\u7070\u7071\u7072\u7073\u7074\u7075\u7076\u7077\u7078\u7079\u707a\u707b\u707c\u707d\u707e\u707f\u7080\u7081\u7082\u7083\u7084\u7085\u7086\u7087\u7088\u7089\u708a\u708b\u708c\u708d\u708e\u708f\u7090\u7091\u7092\u7093\u7094\u7095\u7096\u7097\u7098\u7099\u709a\u709b\u709c\u709d\u709e\u709f\u70a0\u70a1\u70a2\u70a3\u70a4\u70a5\u70a6\u70a7\u70a8\u70a9\u70aa\u70ab\u70ac\u70ad\u70ae\u70af\u70b0\u70b1\u70b2\u70b3\u70b4\u70b5\u70b6\u70b7\u70b8\u70b9\u70ba\u70bb\u70bc\u70bd\u70be\u70bf\u70c0\u70c1\u70c2\u70c3\u70c4\u70c5\u70c6\u70c7\u70c8\u70c9\u70ca\u70cb\u70cc\u70cd\u70ce\u70cf\u70d0\u70d1\u70d2\u70d3\u70d4\u70d5\u70d6\u70d7\u70d8\u70d9\u70da\u70db\u70dc\u70dd\u70de\u70df\u70e0\u70e1\u70e2\u70e3\u70e4\u70e5\u70e6\u70e7\u70e8\u70e9\u70ea\u70eb\u70ec\u70ed\u70ee\u70ef\u70f0\u70f1\u70f2\u70f3\u70f4\u70f5\u70f6\u70f7\u70f8\u70f9\u70fa\u70fb\u70fc\u70fd\u70fe\u70ff\u7100\u7101\u7102\u7103\u7104\u7105\u7106\u7107\u7108\u7109\u710a\u710b\u710c\u710d\u710e\u710f\u7110\u7111\u7112\u7113\u7114\u7115\u7116\u7117\u7118\u7119\u711a\u711b\u711c\u711d\u711e\u711f\u7120\u7121\u7122\u7123\u7124\u7125\u7126\u7127\u7128\u7129\u712a\u712b\u712c\u712d\u712e\u712f\u7130\u7131\u7132\u7133\u7134\u7135\u7136\u7137\u7138\u7139\u713a\u713b\u713c\u713d\u713e\u713f\u7140\u7141\u7142\u7143\u7144\u7145\u7146\u7147\u7148\u7149\u714a\u714b\u714c\u714d\u714e\u714f\u7150\u7151\u7152\u7153\u7154\u7155\u7156\u7157\u7158\u7159\u715a\u715b\u715c\u715d\u715e\u715f\u7160\u7161\u7162\u7163\u7164\u7165\u7166\u7167\u7168\u7169\u716a\u716b\u716c\u716d\u716e\u716f\u7170\u7171\u7172\u7173\u7174\u7175\u7176\u7177\u7178\u7179\u717a\u717b\u717c\u717d\u717e\u717f\u7180\u7181\u7182\u7183\u7184\u7185\u7186\u7187\u7188\u7189\u718a\u718b\u718c\u718d\u718e\u718f\u7190\u7191\u7192\u7193\u7194\u7195\u7196\u7197\u7198\u7199\u719a\u719b\u719c\u719d\u719e\u719f\u71a0\u71a1\u71a2\u71a3\u71a4\u71a5\u71a6\u71a7\u71a8\u71a9\u71aa\u71ab\u71ac\u71ad\u71ae\u71af\u71b0\u71b1\u71b2\u71b3\u71b4\u71b5\u71b6\u71b7\u71b8\u71b9\u71ba\u71bb\u71bc\u71bd\u71be\u71bf\u71c0\u71c1\u71c2\u71c3\u71c4\u71c5\u71c6\u71c7\u71c8\u71c9\u71ca\u71cb\u71cc\u71cd\u71ce\u71cf\u71d0\u71d1\u71d2\u71d3\u71d4\u71d5\u71d6\u71d7\u71d8\u71d9\u71da\u71db\u71dc\u71dd\u71de\u71df\u71e0\u71e1\u71e2\u71e3\u71e4\u71e5\u71e6\u71e7\u71e8\u71e9\u71ea\u71eb\u71ec\u71ed\u71ee\u71ef\u71f0\u71f1\u71f2\u71f3\u71f4\u71f5\u71f6\u71f7\u71f8\u71f9\u71fa\u71fb\u71fc\u71fd\u71fe\u71ff\u7200\u7201\u7202\u7203\u7204\u7205\u7206\u7207\u7208\u7209\u720a\u720b\u720c\u720d\u720e\u720f\u7210\u7211\u7212\u7213\u7214\u7215\u7216\u7217\u7218\u7219\u721a\u721b\u721c\u721d\u721e\u721f\u7220\u7221\u7222\u7223\u7224\u7225\u7226\u7227\u7228\u7229\u722a\u722b\u722c\u722d\u722e\u722f\u7230\u7231\u7232\u7233\u7234\u7235\u7236\u7237\u7238\u7239\u723a\u723b\u723c\u723d\u723e\u723f\u7240\u7241\u7242\u7243\u7244\u7245\u7246\u7247\u7248\u7249\u724a\u724b\u724c\u724d\u724e\u724f\u7250\u7251\u7252\u7253\u7254\u7255\u7256\u7257\u7258\u7259\u725a\u725b\u725c\u725d\u725e\u725f\u7260\u7261\u7262\u7263\u7264\u7265\u7266\u7267\u7268\u7269\u726a\u726b\u726c\u726d\u726e\u726f\u7270\u7271\u7272\u7273\u7274\u7275\u7276\u7277\u7278\u7279\u727a\u727b\u727c\u727d\u727e\u727f\u7280\u7281\u7282\u7283\u7284\u7285\u7286\u7287\u7288\u7289\u728a\u728b\u728c\u728d\u728e\u728f\u7290\u7291\u7292\u7293\u7294\u7295\u7296\u7297\u7298\u7299\u729a\u729b\u729c\u729d\u729e\u729f\u72a0\u72a1\u72a2\u72a3\u72a4\u72a5\u72a6\u72a7\u72a8\u72a9\u72aa\u72ab\u72ac\u72ad\u72ae\u72af\u72b0\u72b1\u72b2\u72b3\u72b4\u72b5\u72b6\u72b7\u72b8\u72b9\u72ba\u72bb\u72bc\u72bd\u72be\u72bf\u72c0\u72c1\u72c2\u72c3\u72c4\u72c5\u72c6\u72c7\u72c8\u72c9\u72ca\u72cb\u72cc\u72cd\u72ce\u72cf\u72d0\u72d1\u72d2\u72d3\u72d4\u72d5\u72d6\u72d7\u72d8\u72d9\u72da\u72db\u72dc\u72dd\u72de\u72df\u72e0\u72e1\u72e2\u72e3\u72e4\u72e5\u72e6\u72e7\u72e8\u72e9\u72ea\u72eb\u72ec\u72ed\u72ee\u72ef\u72f0\u72f1\u72f2\u72f3\u72f4\u72f5\u72f6\u72f7\u72f8\u72f9\u72fa\u72fb\u72fc\u72fd\u72fe\u72ff\u7300\u7301\u7302\u7303\u7304\u7305\u7306\u7307\u7308\u7309\u730a\u730b\u730c\u730d\u730e\u730f\u7310\u7311\u7312\u7313\u7314\u7315\u7316\u7317\u7318\u7319\u731a\u731b\u731c\u731d\u731e\u731f\u7320\u7321\u7322\u7323\u7324\u7325\u7326\u7327\u7328\u7329\u732a\u732b\u732c\u732d\u732e\u732f\u7330\u7331\u7332\u7333\u7334\u7335\u7336\u7337\u7338\u7339\u733a\u733b\u733c\u733d\u733e\u733f\u7340\u7341\u7342\u7343\u7344\u7345\u7346\u7347\u7348\u7349\u734a\u734b\u734c\u734d\u734e\u734f\u7350\u7351\u7352\u7353\u7354\u7355\u7356\u7357\u7358\u7359\u735a\u735b\u735c\u735d\u735e\u735f\u7360\u7361\u7362\u7363\u7364\u7365\u7366\u7367\u7368\u7369\u736a\u736b\u736c\u736d\u736e\u736f\u7370\u7371\u7372\u7373\u7374\u7375\u7376\u7377\u7378\u7379\u737a\u737b\u737c\u737d\u737e\u737f\u7380\u7381\u7382\u7383\u7384\u7385\u7386\u7387\u7388\u7389\u738a\u738b\u738c\u738d\u738e\u738f\u7390\u7391\u7392\u7393\u7394\u7395\u7396\u7397\u7398\u7399\u739a\u739b\u739c\u739d\u739e\u739f\u73a0\u73a1\u73a2\u73a3\u73a4\u73a5\u73a6\u73a7\u73a8\u73a9\u73aa\u73ab\u73ac\u73ad\u73ae\u73af\u73b0\u73b1\u73b2\u73b3\u73b4\u73b5\u73b6\u73b7\u73b8\u73b9\u73ba\u73bb\u73bc\u73bd\u73be\u73bf\u73c0\u73c1\u73c2\u73c3\u73c4\u73c5\u73c6\u73c7\u73c8\u73c9\u73ca\u73cb\u73cc\u73cd\u73ce\u73cf\u73d0\u73d1\u73d2\u73d3\u73d4\u73d5\u73d6\u73d7\u73d8\u73d9\u73da\u73db\u73dc\u73dd\u73de\u73df\u73e0\u73e1\u73e2\u73e3\u73e4\u73e5\u73e6\u73e7\u73e8\u73e9\u73ea\u73eb\u73ec\u73ed\u73ee\u73ef\u73f0\u73f1\u73f2\u73f3\u73f4\u73f5\u73f6\u73f7\u73f8\u73f9\u73fa\u73fb\u73fc\u73fd\u73fe\u73ff\u7400\u7401\u7402\u7403\u7404\u7405\u7406\u7407\u7408\u7409\u740a\u740b\u740c\u740d\u740e\u740f\u7410\u7411\u7412\u7413\u7414\u7415\u7416\u7417\u7418\u7419\u741a\u741b\u741c\u741d\u741e\u741f\u7420\u7421\u7422\u7423\u7424\u7425\u7426\u7427\u7428\u7429\u742a\u742b\u742c\u742d\u742e\u742f\u7430\u7431\u7432\u7433\u7434\u7435\u7436\u7437\u7438\u7439\u743a\u743b\u743c\u743d\u743e\u743f\u7440\u7441\u7442\u7443\u7444\u7445\u7446\u7447\u7448\u7449\u744a\u744b\u744c\u744d\u744e\u744f\u7450\u7451\u7452\u7453\u7454\u7455\u7456\u7457\u7458\u7459\u745a\u745b\u745c\u745d\u745e\u745f\u7460\u7461\u7462\u7463\u7464\u7465\u7466\u7467\u7468\u7469\u746a\u746b\u746c\u746d\u746e\u746f\u7470\u7471\u7472\u7473\u7474\u7475\u7476\u7477\u7478\u7479\u747a\u747b\u747c\u747d\u747e\u747f\u7480\u7481\u7482\u7483\u7484\u7485\u7486\u7487\u7488\u7489\u748a\u748b\u748c\u748d\u748e\u748f\u7490\u7491\u7492\u7493\u7494\u7495\u7496\u7497\u7498\u7499\u749a\u749b\u749c\u749d\u749e\u749f\u74a0\u74a1\u74a2\u74a3\u74a4\u74a5\u74a6\u74a7\u74a8\u74a9\u74aa\u74ab\u74ac\u74ad\u74ae\u74af\u74b0\u74b1\u74b2\u74b3\u74b4\u74b5\u74b6\u74b7\u74b8\u74b9\u74ba\u74bb\u74bc\u74bd\u74be\u74bf\u74c0\u74c1\u74c2\u74c3\u74c4\u74c5\u74c6\u74c7\u74c8\u74c9\u74ca\u74cb\u74cc\u74cd\u74ce\u74cf\u74d0\u74d1\u74d2\u74d3\u74d4\u74d5\u74d6\u74d7\u74d8\u74d9\u74da\u74db\u74dc\u74dd\u74de\u74df\u74e0\u74e1\u74e2\u74e3\u74e4\u74e5\u74e6\u74e7\u74e8\u74e9\u74ea\u74eb\u74ec\u74ed\u74ee\u74ef\u74f0\u74f1\u74f2\u74f3\u74f4\u74f5\u74f6\u74f7\u74f8\u74f9\u74fa\u74fb\u74fc\u74fd\u74fe\u74ff\u7500\u7501\u7502\u7503\u7504\u7505\u7506\u7507\u7508\u7509\u750a\u750b\u750c\u750d\u750e\u750f\u7510\u7511\u7512\u7513\u7514\u7515\u7516\u7517\u7518\u7519\u751a\u751b\u751c\u751d\u751e\u751f\u7520\u7521\u7522\u7523\u7524\u7525\u7526\u7527\u7528\u7529\u752a\u752b\u752c\u752d\u752e\u752f\u7530\u7531\u7532\u7533\u7534\u7535\u7536\u7537\u7538\u7539\u753a\u753b\u753c\u753d\u753e\u753f\u7540\u7541\u7542\u7543\u7544\u7545\u7546\u7547\u7548\u7549\u754a\u754b\u754c\u754d\u754e\u754f\u7550\u7551\u7552\u7553\u7554\u7555\u7556\u7557\u7558\u7559\u755a\u755b\u755c\u755d\u755e\u755f\u7560\u7561\u7562\u7563\u7564\u7565\u7566\u7567\u7568\u7569\u756a\u756b\u756c\u756d\u756e\u756f\u7570\u7571\u7572\u7573\u7574\u7575\u7576\u7577\u7578\u7579\u757a\u757b\u757c\u757d\u757e\u757f\u7580\u7581\u7582\u7583\u7584\u7585\u7586\u7587\u7588\u7589\u758a\u758b\u758c\u758d\u758e\u758f\u7590\u7591\u7592\u7593\u7594\u7595\u7596\u7597\u7598\u7599\u759a\u759b\u759c\u759d\u759e\u759f\u75a0\u75a1\u75a2\u75a3\u75a4\u75a5\u75a6\u75a7\u75a8\u75a9\u75aa\u75ab\u75ac\u75ad\u75ae\u75af\u75b0\u75b1\u75b2\u75b3\u75b4\u75b5\u75b6\u75b7\u75b8\u75b9\u75ba\u75bb\u75bc\u75bd\u75be\u75bf\u75c0\u75c1\u75c2\u75c3\u75c4\u75c5\u75c6\u75c7\u75c8\u75c9\u75ca\u75cb\u75cc\u75cd\u75ce\u75cf\u75d0\u75d1\u75d2\u75d3\u75d4\u75d5\u75d6\u75d7\u75d8\u75d9\u75da\u75db\u75dc\u75dd\u75de\u75df\u75e0\u75e1\u75e2\u75e3\u75e4\u75e5\u75e6\u75e7\u75e8\u75e9\u75ea\u75eb\u75ec\u75ed\u75ee\u75ef\u75f0\u75f1\u75f2\u75f3\u75f4\u75f5\u75f6\u75f7\u75f8\u75f9\u75fa\u75fb\u75fc\u75fd\u75fe\u75ff\u7600\u7601\u7602\u7603\u7604\u7605\u7606\u7607\u7608\u7609\u760a\u760b\u760c\u760d\u760e\u760f\u7610\u7611\u7612\u7613\u7614\u7615\u7616\u7617\u7618\u7619\u761a\u761b\u761c\u761d\u761e\u761f\u7620\u7621\u7622\u7623\u7624\u7625\u7626\u7627\u7628\u7629\u762a\u762b\u762c\u762d\u762e\u762f\u7630\u7631\u7632\u7633\u7634\u7635\u7636\u7637\u7638\u7639\u763a\u763b\u763c\u763d\u763e\u763f\u7640\u7641\u7642\u7643\u7644\u7645\u7646\u7647\u7648\u7649\u764a\u764b\u764c\u764d\u764e\u764f\u7650\u7651\u7652\u7653\u7654\u7655\u7656\u7657\u7658\u7659\u765a\u765b\u765c\u765d\u765e\u765f\u7660\u7661\u7662\u7663\u7664\u7665\u7666\u7667\u7668\u7669\u766a\u766b\u766c\u766d\u766e\u766f\u7670\u7671\u7672\u7673\u7674\u7675\u7676\u7677\u7678\u7679\u767a\u767b\u767c\u767d\u767e\u767f\u7680\u7681\u7682\u7683\u7684\u7685\u7686\u7687\u7688\u7689\u768a\u768b\u768c\u768d\u768e\u768f\u7690\u7691\u7692\u7693\u7694\u7695\u7696\u7697\u7698\u7699\u769a\u769b\u769c\u769d\u769e\u769f\u76a0\u76a1\u76a2\u76a3\u76a4\u76a5\u76a6\u76a7\u76a8\u76a9\u76aa\u76ab\u76ac\u76ad\u76ae\u76af\u76b0\u76b1\u76b2\u76b3\u76b4\u76b5\u76b6\u76b7\u76b8\u76b9\u76ba\u76bb\u76bc\u76bd\u76be\u76bf\u76c0\u76c1\u76c2\u76c3\u76c4\u76c5\u76c6\u76c7\u76c8\u76c9\u76ca\u76cb\u76cc\u76cd\u76ce\u76cf\u76d0\u76d1\u76d2\u76d3\u76d4\u76d5\u76d6\u76d7\u76d8\u76d9\u76da\u76db\u76dc\u76dd\u76de\u76df\u76e0\u76e1\u76e2\u76e3\u76e4\u76e5\u76e6\u76e7\u76e8\u76e9\u76ea\u76eb\u76ec\u76ed\u76ee\u76ef\u76f0\u76f1\u76f2\u76f3\u76f4\u76f5\u76f6\u76f7\u76f8\u76f9\u76fa\u76fb\u76fc\u76fd\u76fe\u76ff\u7700\u7701\u7702\u7703\u7704\u7705\u7706\u7707\u7708\u7709\u770a\u770b\u770c\u770d\u770e\u770f\u7710\u7711\u7712\u7713\u7714\u7715\u7716\u7717\u7718\u7719\u771a\u771b\u771c\u771d\u771e\u771f\u7720\u7721\u7722\u7723\u7724\u7725\u7726\u7727\u7728\u7729\u772a\u772b\u772c\u772d\u772e\u772f\u7730\u7731\u7732\u7733\u7734\u7735\u7736\u7737\u7738\u7739\u773a\u773b\u773c\u773d\u773e\u773f\u7740\u7741\u7742\u7743\u7744\u7745\u7746\u7747\u7748\u7749\u774a\u774b\u774c\u774d\u774e\u774f\u7750\u7751\u7752\u7753\u7754\u7755\u7756\u7757\u7758\u7759\u775a\u775b\u775c\u775d\u775e\u775f\u7760\u7761\u7762\u7763\u7764\u7765\u7766\u7767\u7768\u7769\u776a\u776b\u776c\u776d\u776e\u776f\u7770\u7771\u7772\u7773\u7774\u7775\u7776\u7777\u7778\u7779\u777a\u777b\u777c\u777d\u777e\u777f\u7780\u7781\u7782\u7783\u7784\u7785\u7786\u7787\u7788\u7789\u778a\u778b\u778c\u778d\u778e\u778f\u7790\u7791\u7792\u7793\u7794\u7795\u7796\u7797\u7798\u7799\u779a\u779b\u779c\u779d\u779e\u779f\u77a0\u77a1\u77a2\u77a3\u77a4\u77a5\u77a6\u77a7\u77a8\u77a9\u77aa\u77ab\u77ac\u77ad\u77ae\u77af\u77b0\u77b1\u77b2\u77b3\u77b4\u77b5\u77b6\u77b7\u77b8\u77b9\u77ba\u77bb\u77bc\u77bd\u77be\u77bf\u77c0\u77c1\u77c2\u77c3\u77c4\u77c5\u77c6\u77c7\u77c8\u77c9\u77ca\u77cb\u77cc\u77cd\u77ce\u77cf\u77d0\u77d1\u77d2\u77d3\u77d4\u77d5\u77d6\u77d7\u77d8\u77d9\u77da\u77db\u77dc\u77dd\u77de\u77df\u77e0\u77e1\u77e2\u77e3\u77e4\u77e5\u77e6\u77e7\u77e8\u77e9\u77ea\u77eb\u77ec\u77ed\u77ee\u77ef\u77f0\u77f1\u77f2\u77f3\u77f4\u77f5\u77f6\u77f7\u77f8\u77f9\u77fa\u77fb\u77fc\u77fd\u77fe\u77ff\u7800\u7801\u7802\u7803\u7804\u7805\u7806\u7807\u7808\u7809\u780a\u780b\u780c\u780d\u780e\u780f\u7810\u7811\u7812\u7813\u7814\u7815\u7816\u7817\u7818\u7819\u781a\u781b\u781c\u781d\u781e\u781f\u7820\u7821\u7822\u7823\u7824\u7825\u7826\u7827\u7828\u7829\u782a\u782b\u782c\u782d\u782e\u782f\u7830\u7831\u7832\u7833\u7834\u7835\u7836\u7837\u7838\u7839\u783a\u783b\u783c\u783d\u783e\u783f\u7840\u7841\u7842\u7843\u7844\u7845\u7846\u7847\u7848\u7849\u784a\u784b\u784c\u784d\u784e\u784f\u7850\u7851\u7852\u7853\u7854\u7855\u7856\u7857\u7858\u7859\u785a\u785b\u785c\u785d\u785e\u785f\u7860\u7861\u7862\u7863\u7864\u7865\u7866\u7867\u7868\u7869\u786a\u786b\u786c\u786d\u786e\u786f\u7870\u7871\u7872\u7873\u7874\u7875\u7876\u7877\u7878\u7879\u787a\u787b\u787c\u787d\u787e\u787f\u7880\u7881\u7882\u7883\u7884\u7885\u7886\u7887\u7888\u7889\u788a\u788b\u788c\u788d\u788e\u788f\u7890\u7891\u7892\u7893\u7894\u7895\u7896\u7897\u7898\u7899\u789a\u789b\u789c\u789d\u789e\u789f\u78a0\u78a1\u78a2\u78a3\u78a4\u78a5\u78a6\u78a7\u78a8\u78a9\u78aa\u78ab\u78ac\u78ad\u78ae\u78af\u78b0\u78b1\u78b2\u78b3\u78b4\u78b5\u78b6\u78b7\u78b8\u78b9\u78ba\u78bb\u78bc\u78bd\u78be\u78bf\u78c0\u78c1\u78c2\u78c3\u78c4\u78c5\u78c6\u78c7\u78c8\u78c9\u78ca\u78cb\u78cc\u78cd\u78ce\u78cf\u78d0\u78d1\u78d2\u78d3\u78d4\u78d5\u78d6\u78d7\u78d8\u78d9\u78da\u78db\u78dc\u78dd\u78de\u78df\u78e0\u78e1\u78e2\u78e3\u78e4\u78e5\u78e6\u78e7\u78e8\u78e9\u78ea\u78eb\u78ec\u78ed\u78ee\u78ef\u78f0\u78f1\u78f2\u78f3\u78f4\u78f5\u78f6\u78f7\u78f8\u78f9\u78fa\u78fb\u78fc\u78fd\u78fe\u78ff\u7900\u7901\u7902\u7903\u7904\u7905\u7906\u7907\u7908\u7909\u790a\u790b\u790c\u790d\u790e\u790f\u7910\u7911\u7912\u7913\u7914\u7915\u7916\u7917\u7918\u7919\u791a\u791b\u791c\u791d\u791e\u791f\u7920\u7921\u7922\u7923\u7924\u7925\u7926\u7927\u7928\u7929\u792a\u792b\u792c\u792d\u792e\u792f\u7930\u7931\u7932\u7933\u7934\u7935\u7936\u7937\u7938\u7939\u793a\u793b\u793c\u793d\u793e\u793f\u7940\u7941\u7942\u7943\u7944\u7945\u7946\u7947\u7948\u7949\u794a\u794b\u794c\u794d\u794e\u794f\u7950\u7951\u7952\u7953\u7954\u7955\u7956\u7957\u7958\u7959\u795a\u795b\u795c\u795d\u795e\u795f\u7960\u7961\u7962\u7963\u7964\u7965\u7966\u7967\u7968\u7969\u796a\u796b\u796c\u796d\u796e\u796f\u7970\u7971\u7972\u7973\u7974\u7975\u7976\u7977\u7978\u7979\u797a\u797b\u797c\u797d\u797e\u797f\u7980\u7981\u7982\u7983\u7984\u7985\u7986\u7987\u7988\u7989\u798a\u798b\u798c\u798d\u798e\u798f\u7990\u7991\u7992\u7993\u7994\u7995\u7996\u7997\u7998\u7999\u799a\u799b\u799c\u799d\u799e\u799f\u79a0\u79a1\u79a2\u79a3\u79a4\u79a5\u79a6\u79a7\u79a8\u79a9\u79aa\u79ab\u79ac\u79ad\u79ae\u79af\u79b0\u79b1\u79b2\u79b3\u79b4\u79b5\u79b6\u79b7\u79b8\u79b9\u79ba\u79bb\u79bc\u79bd\u79be\u79bf\u79c0\u79c1\u79c2\u79c3\u79c4\u79c5\u79c6\u79c7\u79c8\u79c9\u79ca\u79cb\u79cc\u79cd\u79ce\u79cf\u79d0\u79d1\u79d2\u79d3\u79d4\u79d5\u79d6\u79d7\u79d8\u79d9\u79da\u79db\u79dc\u79dd\u79de\u79df\u79e0\u79e1\u79e2\u79e3\u79e4\u79e5\u79e6\u79e7\u79e8\u79e9\u79ea\u79eb\u79ec\u79ed\u79ee\u79ef\u79f0\u79f1\u79f2\u79f3\u79f4\u79f5\u79f6\u79f7\u79f8\u79f9\u79fa\u79fb\u79fc\u79fd\u79fe\u79ff\u7a00\u7a01\u7a02\u7a03\u7a04\u7a05\u7a06\u7a07\u7a08\u7a09\u7a0a\u7a0b\u7a0c\u7a0d\u7a0e\u7a0f\u7a10\u7a11\u7a12\u7a13\u7a14\u7a15\u7a16\u7a17\u7a18\u7a19\u7a1a\u7a1b\u7a1c\u7a1d\u7a1e\u7a1f\u7a20\u7a21\u7a22\u7a23\u7a24\u7a25\u7a26\u7a27\u7a28\u7a29\u7a2a\u7a2b\u7a2c\u7a2d\u7a2e\u7a2f\u7a30\u7a31\u7a32\u7a33\u7a34\u7a35\u7a36\u7a37\u7a38\u7a39\u7a3a\u7a3b\u7a3c\u7a3d\u7a3e\u7a3f\u7a40\u7a41\u7a42\u7a43\u7a44\u7a45\u7a46\u7a47\u7a48\u7a49\u7a4a\u7a4b\u7a4c\u7a4d\u7a4e\u7a4f\u7a50\u7a51\u7a52\u7a53\u7a54\u7a55\u7a56\u7a57\u7a58\u7a59\u7a5a\u7a5b\u7a5c\u7a5d\u7a5e\u7a5f\u7a60\u7a61\u7a62\u7a63\u7a64\u7a65\u7a66\u7a67\u7a68\u7a69\u7a6a\u7a6b\u7a6c\u7a6d\u7a6e\u7a6f\u7a70\u7a71\u7a72\u7a73\u7a74\u7a75\u7a76\u7a77\u7a78\u7a79\u7a7a\u7a7b\u7a7c\u7a7d\u7a7e\u7a7f\u7a80\u7a81\u7a82\u7a83\u7a84\u7a85\u7a86\u7a87\u7a88\u7a89\u7a8a\u7a8b\u7a8c\u7a8d\u7a8e\u7a8f\u7a90\u7a91\u7a92\u7a93\u7a94\u7a95\u7a96\u7a97\u7a98\u7a99\u7a9a\u7a9b\u7a9c\u7a9d\u7a9e\u7a9f\u7aa0\u7aa1\u7aa2\u7aa3\u7aa4\u7aa5\u7aa6\u7aa7\u7aa8\u7aa9\u7aaa\u7aab\u7aac\u7aad\u7aae\u7aaf\u7ab0\u7ab1\u7ab2\u7ab3\u7ab4\u7ab5\u7ab6\u7ab7\u7ab8\u7ab9\u7aba\u7abb\u7abc\u7abd\u7abe\u7abf\u7ac0\u7ac1\u7ac2\u7ac3\u7ac4\u7ac5\u7ac6\u7ac7\u7ac8\u7ac9\u7aca\u7acb\u7acc\u7acd\u7ace\u7acf\u7ad0\u7ad1\u7ad2\u7ad3\u7ad4\u7ad5\u7ad6\u7ad7\u7ad8\u7ad9\u7ada\u7adb\u7adc\u7add\u7ade\u7adf\u7ae0\u7ae1\u7ae2\u7ae3\u7ae4\u7ae5\u7ae6\u7ae7\u7ae8\u7ae9\u7aea\u7aeb\u7aec\u7aed\u7aee\u7aef\u7af0\u7af1\u7af2\u7af3\u7af4\u7af5\u7af6\u7af7\u7af8\u7af9\u7afa\u7afb\u7afc\u7afd\u7afe\u7aff\u7b00\u7b01\u7b02\u7b03\u7b04\u7b05\u7b06\u7b07\u7b08\u7b09\u7b0a\u7b0b\u7b0c\u7b0d\u7b0e\u7b0f\u7b10\u7b11\u7b12\u7b13\u7b14\u7b15\u7b16\u7b17\u7b18\u7b19\u7b1a\u7b1b\u7b1c\u7b1d\u7b1e\u7b1f\u7b20\u7b21\u7b22\u7b23\u7b24\u7b25\u7b26\u7b27\u7b28\u7b29\u7b2a\u7b2b\u7b2c\u7b2d\u7b2e\u7b2f\u7b30\u7b31\u7b32\u7b33\u7b34\u7b35\u7b36\u7b37\u7b38\u7b39\u7b3a\u7b3b\u7b3c\u7b3d\u7b3e\u7b3f\u7b40\u7b41\u7b42\u7b43\u7b44\u7b45\u7b46\u7b47\u7b48\u7b49\u7b4a\u7b4b\u7b4c\u7b4d\u7b4e\u7b4f\u7b50\u7b51\u7b52\u7b53\u7b54\u7b55\u7b56\u7b57\u7b58\u7b59\u7b5a\u7b5b\u7b5c\u7b5d\u7b5e\u7b5f\u7b60\u7b61\u7b62\u7b63\u7b64\u7b65\u7b66\u7b67\u7b68\u7b69\u7b6a\u7b6b\u7b6c\u7b6d\u7b6e\u7b6f\u7b70\u7b71\u7b72\u7b73\u7b74\u7b75\u7b76\u7b77\u7b78\u7b79\u7b7a\u7b7b\u7b7c\u7b7d\u7b7e\u7b7f\u7b80\u7b81\u7b82\u7b83\u7b84\u7b85\u7b86\u7b87\u7b88\u7b89\u7b8a\u7b8b\u7b8c\u7b8d\u7b8e\u7b8f\u7b90\u7b91\u7b92\u7b93\u7b94\u7b95\u7b96\u7b97\u7b98\u7b99\u7b9a\u7b9b\u7b9c\u7b9d\u7b9e\u7b9f\u7ba0\u7ba1\u7ba2\u7ba3\u7ba4\u7ba5\u7ba6\u7ba7\u7ba8\u7ba9\u7baa\u7bab\u7bac\u7bad\u7bae\u7baf\u7bb0\u7bb1\u7bb2\u7bb3\u7bb4\u7bb5\u7bb6\u7bb7\u7bb8\u7bb9\u7bba\u7bbb\u7bbc\u7bbd\u7bbe\u7bbf\u7bc0\u7bc1\u7bc2\u7bc3\u7bc4\u7bc5\u7bc6\u7bc7\u7bc8\u7bc9\u7bca\u7bcb\u7bcc\u7bcd\u7bce\u7bcf\u7bd0\u7bd1\u7bd2\u7bd3\u7bd4\u7bd5\u7bd6\u7bd7\u7bd8\u7bd9\u7bda\u7bdb\u7bdc\u7bdd\u7bde\u7bdf\u7be0\u7be1\u7be2\u7be3\u7be4\u7be5\u7be6\u7be7\u7be8\u7be9\u7bea\u7beb\u7bec\u7bed\u7bee\u7bef\u7bf0\u7bf1\u7bf2\u7bf3\u7bf4\u7bf5\u7bf6\u7bf7\u7bf8\u7bf9\u7bfa\u7bfb\u7bfc\u7bfd\u7bfe\u7bff\u7c00\u7c01\u7c02\u7c03\u7c04\u7c05\u7c06\u7c07\u7c08\u7c09\u7c0a\u7c0b\u7c0c\u7c0d\u7c0e\u7c0f\u7c10\u7c11\u7c12\u7c13\u7c14\u7c15\u7c16\u7c17\u7c18\u7c19\u7c1a\u7c1b\u7c1c\u7c1d\u7c1e\u7c1f\u7c20\u7c21\u7c22\u7c23\u7c24\u7c25\u7c26\u7c27\u7c28\u7c29\u7c2a\u7c2b\u7c2c\u7c2d\u7c2e\u7c2f\u7c30\u7c31\u7c32\u7c33\u7c34\u7c35\u7c36\u7c37\u7c38\u7c39\u7c3a\u7c3b\u7c3c\u7c3d\u7c3e\u7c3f\u7c40\u7c41\u7c42\u7c43\u7c44\u7c45\u7c46\u7c47\u7c48\u7c49\u7c4a\u7c4b\u7c4c\u7c4d\u7c4e\u7c4f\u7c50\u7c51\u7c52\u7c53\u7c54\u7c55\u7c56\u7c57\u7c58\u7c59\u7c5a\u7c5b\u7c5c\u7c5d\u7c5e\u7c5f\u7c60\u7c61\u7c62\u7c63\u7c64\u7c65\u7c66\u7c67\u7c68\u7c69\u7c6a\u7c6b\u7c6c\u7c6d\u7c6e\u7c6f\u7c70\u7c71\u7c72\u7c73\u7c74\u7c75\u7c76\u7c77\u7c78\u7c79\u7c7a\u7c7b\u7c7c\u7c7d\u7c7e\u7c7f\u7c80\u7c81\u7c82\u7c83\u7c84\u7c85\u7c86\u7c87\u7c88\u7c89\u7c8a\u7c8b\u7c8c\u7c8d\u7c8e\u7c8f\u7c90\u7c91\u7c92\u7c93\u7c94\u7c95\u7c96\u7c97\u7c98\u7c99\u7c9a\u7c9b\u7c9c\u7c9d\u7c9e\u7c9f\u7ca0\u7ca1\u7ca2\u7ca3\u7ca4\u7ca5\u7ca6\u7ca7\u7ca8\u7ca9\u7caa\u7cab\u7cac\u7cad\u7cae\u7caf\u7cb0\u7cb1\u7cb2\u7cb3\u7cb4\u7cb5\u7cb6\u7cb7\u7cb8\u7cb9\u7cba\u7cbb\u7cbc\u7cbd\u7cbe\u7cbf\u7cc0\u7cc1\u7cc2\u7cc3\u7cc4\u7cc5\u7cc6\u7cc7\u7cc8\u7cc9\u7cca\u7ccb\u7ccc\u7ccd\u7cce\u7ccf\u7cd0\u7cd1\u7cd2\u7cd3\u7cd4\u7cd5\u7cd6\u7cd7\u7cd8\u7cd9\u7cda\u7cdb\u7cdc\u7cdd\u7cde\u7cdf\u7ce0\u7ce1\u7ce2\u7ce3\u7ce4\u7ce5\u7ce6\u7ce7\u7ce8\u7ce9\u7cea\u7ceb\u7cec\u7ced\u7cee\u7cef\u7cf0\u7cf1\u7cf2\u7cf3\u7cf4\u7cf5\u7cf6\u7cf7\u7cf8\u7cf9\u7cfa\u7cfb\u7cfc\u7cfd\u7cfe\u7cff\u7d00\u7d01\u7d02\u7d03\u7d04\u7d05\u7d06\u7d07\u7d08\u7d09\u7d0a\u7d0b\u7d0c\u7d0d\u7d0e\u7d0f\u7d10\u7d11\u7d12\u7d13\u7d14\u7d15\u7d16\u7d17\u7d18\u7d19\u7d1a\u7d1b\u7d1c\u7d1d\u7d1e\u7d1f\u7d20\u7d21\u7d22\u7d23\u7d24\u7d25\u7d26\u7d27\u7d28\u7d29\u7d2a\u7d2b\u7d2c\u7d2d\u7d2e\u7d2f\u7d30\u7d31\u7d32\u7d33\u7d34\u7d35\u7d36\u7d37\u7d38\u7d39\u7d3a\u7d3b\u7d3c\u7d3d\u7d3e\u7d3f\u7d40\u7d41\u7d42\u7d43\u7d44\u7d45\u7d46\u7d47\u7d48\u7d49\u7d4a\u7d4b\u7d4c\u7d4d\u7d4e\u7d4f\u7d50\u7d51\u7d52\u7d53\u7d54\u7d55\u7d56\u7d57\u7d58\u7d59\u7d5a\u7d5b\u7d5c\u7d5d\u7d5e\u7d5f\u7d60\u7d61\u7d62\u7d63\u7d64\u7d65\u7d66\u7d67\u7d68\u7d69\u7d6a\u7d6b\u7d6c\u7d6d\u7d6e\u7d6f\u7d70\u7d71\u7d72\u7d73\u7d74\u7d75\u7d76\u7d77\u7d78\u7d79\u7d7a\u7d7b\u7d7c\u7d7d\u7d7e\u7d7f\u7d80\u7d81\u7d82\u7d83\u7d84\u7d85\u7d86\u7d87\u7d88\u7d89\u7d8a\u7d8b\u7d8c\u7d8d\u7d8e\u7d8f\u7d90\u7d91\u7d92\u7d93\u7d94\u7d95\u7d96\u7d97\u7d98\u7d99\u7d9a\u7d9b\u7d9c\u7d9d\u7d9e\u7d9f\u7da0\u7da1\u7da2\u7da3\u7da4\u7da5\u7da6\u7da7\u7da8\u7da9\u7daa\u7dab\u7dac\u7dad\u7dae\u7daf\u7db0\u7db1\u7db2\u7db3\u7db4\u7db5\u7db6\u7db7\u7db8\u7db9\u7dba\u7dbb\u7dbc\u7dbd\u7dbe\u7dbf\u7dc0\u7dc1\u7dc2\u7dc3\u7dc4\u7dc5\u7dc6\u7dc7\u7dc8\u7dc9\u7dca\u7dcb\u7dcc\u7dcd\u7dce\u7dcf\u7dd0\u7dd1\u7dd2\u7dd3\u7dd4\u7dd5\u7dd6\u7dd7\u7dd8\u7dd9\u7dda\u7ddb\u7ddc\u7ddd\u7dde\u7ddf\u7de0\u7de1\u7de2\u7de3\u7de4\u7de5\u7de6\u7de7\u7de8\u7de9\u7dea\u7deb\u7dec\u7ded\u7dee\u7def\u7df0\u7df1\u7df2\u7df3\u7df4\u7df5\u7df6\u7df7\u7df8\u7df9\u7dfa\u7dfb\u7dfc\u7dfd\u7dfe\u7dff\u7e00\u7e01\u7e02\u7e03\u7e04\u7e05\u7e06\u7e07\u7e08\u7e09\u7e0a\u7e0b\u7e0c\u7e0d\u7e0e\u7e0f\u7e10\u7e11\u7e12\u7e13\u7e14\u7e15\u7e16\u7e17\u7e18\u7e19\u7e1a\u7e1b\u7e1c\u7e1d\u7e1e\u7e1f\u7e20\u7e21\u7e22\u7e23\u7e24\u7e25\u7e26\u7e27\u7e28\u7e29\u7e2a\u7e2b\u7e2c\u7e2d\u7e2e\u7e2f\u7e30\u7e31\u7e32\u7e33\u7e34\u7e35\u7e36\u7e37\u7e38\u7e39\u7e3a\u7e3b\u7e3c\u7e3d\u7e3e\u7e3f\u7e40\u7e41\u7e42\u7e43\u7e44\u7e45\u7e46\u7e47\u7e48\u7e49\u7e4a\u7e4b\u7e4c\u7e4d\u7e4e\u7e4f\u7e50\u7e51\u7e52\u7e53\u7e54\u7e55\u7e56\u7e57\u7e58\u7e59\u7e5a\u7e5b\u7e5c\u7e5d\u7e5e\u7e5f\u7e60\u7e61\u7e62\u7e63\u7e64\u7e65\u7e66\u7e67\u7e68\u7e69\u7e6a\u7e6b\u7e6c\u7e6d\u7e6e\u7e6f\u7e70\u7e71\u7e72\u7e73\u7e74\u7e75\u7e76\u7e77\u7e78\u7e79\u7e7a\u7e7b\u7e7c\u7e7d\u7e7e\u7e7f\u7e80\u7e81\u7e82\u7e83\u7e84\u7e85\u7e86\u7e87\u7e88\u7e89\u7e8a\u7e8b\u7e8c\u7e8d\u7e8e\u7e8f\u7e90\u7e91\u7e92\u7e93\u7e94\u7e95\u7e96\u7e97\u7e98\u7e99\u7e9a\u7e9b\u7e9c\u7e9d\u7e9e\u7e9f\u7ea0\u7ea1\u7ea2\u7ea3\u7ea4\u7ea5\u7ea6\u7ea7\u7ea8\u7ea9\u7eaa\u7eab\u7eac\u7ead\u7eae\u7eaf\u7eb0\u7eb1\u7eb2\u7eb3\u7eb4\u7eb5\u7eb6\u7eb7\u7eb8\u7eb9\u7eba\u7ebb\u7ebc\u7ebd\u7ebe\u7ebf\u7ec0\u7ec1\u7ec2\u7ec3\u7ec4\u7ec5\u7ec6\u7ec7\u7ec8\u7ec9\u7eca\u7ecb\u7ecc\u7ecd\u7ece\u7ecf\u7ed0\u7ed1\u7ed2\u7ed3\u7ed4\u7ed5\u7ed6\u7ed7\u7ed8\u7ed9\u7eda\u7edb\u7edc\u7edd\u7ede\u7edf\u7ee0\u7ee1\u7ee2\u7ee3\u7ee4\u7ee5\u7ee6\u7ee7\u7ee8\u7ee9\u7eea\u7eeb\u7eec\u7eed\u7eee\u7eef\u7ef0\u7ef1\u7ef2\u7ef3\u7ef4\u7ef5\u7ef6\u7ef7\u7ef8\u7ef9\u7efa\u7efb\u7efc\u7efd\u7efe\u7eff\u7f00\u7f01\u7f02\u7f03\u7f04\u7f05\u7f06\u7f07\u7f08\u7f09\u7f0a\u7f0b\u7f0c\u7f0d\u7f0e\u7f0f\u7f10\u7f11\u7f12\u7f13\u7f14\u7f15\u7f16\u7f17\u7f18\u7f19\u7f1a\u7f1b\u7f1c\u7f1d\u7f1e\u7f1f\u7f20\u7f21\u7f22\u7f23\u7f24\u7f25\u7f26\u7f27\u7f28\u7f29\u7f2a\u7f2b\u7f2c\u7f2d\u7f2e\u7f2f\u7f30\u7f31\u7f32\u7f33\u7f34\u7f35\u7f36\u7f37\u7f38\u7f39\u7f3a\u7f3b\u7f3c\u7f3d\u7f3e\u7f3f\u7f40\u7f41\u7f42\u7f43\u7f44\u7f45\u7f46\u7f47\u7f48\u7f49\u7f4a\u7f4b\u7f4c\u7f4d\u7f4e\u7f4f\u7f50\u7f51\u7f52\u7f53\u7f54\u7f55\u7f56\u7f57\u7f58\u7f59\u7f5a\u7f5b\u7f5c\u7f5d\u7f5e\u7f5f\u7f60\u7f61\u7f62\u7f63\u7f64\u7f65\u7f66\u7f67\u7f68\u7f69\u7f6a\u7f6b\u7f6c\u7f6d\u7f6e\u7f6f\u7f70\u7f71\u7f72\u7f73\u7f74\u7f75\u7f76\u7f77\u7f78\u7f79\u7f7a\u7f7b\u7f7c\u7f7d\u7f7e\u7f7f\u7f80\u7f81\u7f82\u7f83\u7f84\u7f85\u7f86\u7f87\u7f88\u7f89\u7f8a\u7f8b\u7f8c\u7f8d\u7f8e\u7f8f\u7f90\u7f91\u7f92\u7f93\u7f94\u7f95\u7f96\u7f97\u7f98\u7f99\u7f9a\u7f9b\u7f9c\u7f9d\u7f9e\u7f9f\u7fa0\u7fa1\u7fa2\u7fa3\u7fa4\u7fa5\u7fa6\u7fa7\u7fa8\u7fa9\u7faa\u7fab\u7fac\u7fad\u7fae\u7faf\u7fb0\u7fb1\u7fb2\u7fb3\u7fb4\u7fb5\u7fb6\u7fb7\u7fb8\u7fb9\u7fba\u7fbb\u7fbc\u7fbd\u7fbe\u7fbf\u7fc0\u7fc1\u7fc2\u7fc3\u7fc4\u7fc5\u7fc6\u7fc7\u7fc8\u7fc9\u7fca\u7fcb\u7fcc\u7fcd\u7fce\u7fcf\u7fd0\u7fd1\u7fd2\u7fd3\u7fd4\u7fd5\u7fd6\u7fd7\u7fd8\u7fd9\u7fda\u7fdb\u7fdc\u7fdd\u7fde\u7fdf\u7fe0\u7fe1\u7fe2\u7fe3\u7fe4\u7fe5\u7fe6\u7fe7\u7fe8\u7fe9\u7fea\u7feb\u7fec\u7fed\u7fee\u7fef\u7ff0\u7ff1\u7ff2\u7ff3\u7ff4\u7ff5\u7ff6\u7ff7\u7ff8\u7ff9\u7ffa\u7ffb\u7ffc\u7ffd\u7ffe\u7fff\u8000\u8001\u8002\u8003\u8004\u8005\u8006\u8007\u8008\u8009\u800a\u800b\u800c\u800d\u800e\u800f\u8010\u8011\u8012\u8013\u8014\u8015\u8016\u8017\u8018\u8019\u801a\u801b\u801c\u801d\u801e\u801f\u8020\u8021\u8022\u8023\u8024\u8025\u8026\u8027\u8028\u8029\u802a\u802b\u802c\u802d\u802e\u802f\u8030\u8031\u8032\u8033\u8034\u8035\u8036\u8037\u8038\u8039\u803a\u803b\u803c\u803d\u803e\u803f\u8040\u8041\u8042\u8043\u8044\u8045\u8046\u8047\u8048\u8049\u804a\u804b\u804c\u804d\u804e\u804f\u8050\u8051\u8052\u8053\u8054\u8055\u8056\u8057\u8058\u8059\u805a\u805b\u805c\u805d\u805e\u805f\u8060\u8061\u8062\u8063\u8064\u8065\u8066\u8067\u8068\u8069\u806a\u806b\u806c\u806d\u806e\u806f\u8070\u8071\u8072\u8073\u8074\u8075\u8076\u8077\u8078\u8079\u807a\u807b\u807c\u807d\u807e\u807f\u8080\u8081\u8082\u8083\u8084\u8085\u8086\u8087\u8088\u8089\u808a\u808b\u808c\u808d\u808e\u808f\u8090\u8091\u8092\u8093\u8094\u8095\u8096\u8097\u8098\u8099\u809a\u809b\u809c\u809d\u809e\u809f\u80a0\u80a1\u80a2\u80a3\u80a4\u80a5\u80a6\u80a7\u80a8\u80a9\u80aa\u80ab\u80ac\u80ad\u80ae\u80af\u80b0\u80b1\u80b2\u80b3\u80b4\u80b5\u80b6\u80b7\u80b8\u80b9\u80ba\u80bb\u80bc\u80bd\u80be\u80bf\u80c0\u80c1\u80c2\u80c3\u80c4\u80c5\u80c6\u80c7\u80c8\u80c9\u80ca\u80cb\u80cc\u80cd\u80ce\u80cf\u80d0\u80d1\u80d2\u80d3\u80d4\u80d5\u80d6\u80d7\u80d8\u80d9\u80da\u80db\u80dc\u80dd\u80de\u80df\u80e0\u80e1\u80e2\u80e3\u80e4\u80e5\u80e6\u80e7\u80e8\u80e9\u80ea\u80eb\u80ec\u80ed\u80ee\u80ef\u80f0\u80f1\u80f2\u80f3\u80f4\u80f5\u80f6\u80f7\u80f8\u80f9\u80fa\u80fb\u80fc\u80fd\u80fe\u80ff\u8100\u8101\u8102\u8103\u8104\u8105\u8106\u8107\u8108\u8109\u810a\u810b\u810c\u810d\u810e\u810f\u8110\u8111\u8112\u8113\u8114\u8115\u8116\u8117\u8118\u8119\u811a\u811b\u811c\u811d\u811e\u811f\u8120\u8121\u8122\u8123\u8124\u8125\u8126\u8127\u8128\u8129\u812a\u812b\u812c\u812d\u812e\u812f\u8130\u8131\u8132\u8133\u8134\u8135\u8136\u8137\u8138\u8139\u813a\u813b\u813c\u813d\u813e\u813f\u8140\u8141\u8142\u8143\u8144\u8145\u8146\u8147\u8148\u8149\u814a\u814b\u814c\u814d\u814e\u814f\u8150\u8151\u8152\u8153\u8154\u8155\u8156\u8157\u8158\u8159\u815a\u815b\u815c\u815d\u815e\u815f\u8160\u8161\u8162\u8163\u8164\u8165\u8166\u8167\u8168\u8169\u816a\u816b\u816c\u816d\u816e\u816f\u8170\u8171\u8172\u8173\u8174\u8175\u8176\u8177\u8178\u8179\u817a\u817b\u817c\u817d\u817e\u817f\u8180\u8181\u8182\u8183\u8184\u8185\u8186\u8187\u8188\u8189\u818a\u818b\u818c\u818d\u818e\u818f\u8190\u8191\u8192\u8193\u8194\u8195\u8196\u8197\u8198\u8199\u819a\u819b\u819c\u819d\u819e\u819f\u81a0\u81a1\u81a2\u81a3\u81a4\u81a5\u81a6\u81a7\u81a8\u81a9\u81aa\u81ab\u81ac\u81ad\u81ae\u81af\u81b0\u81b1\u81b2\u81b3\u81b4\u81b5\u81b6\u81b7\u81b8\u81b9\u81ba\u81bb\u81bc\u81bd\u81be\u81bf\u81c0\u81c1\u81c2\u81c3\u81c4\u81c5\u81c6\u81c7\u81c8\u81c9\u81ca\u81cb\u81cc\u81cd\u81ce\u81cf\u81d0\u81d1\u81d2\u81d3\u81d4\u81d5\u81d6\u81d7\u81d8\u81d9\u81da\u81db\u81dc\u81dd\u81de\u81df\u81e0\u81e1\u81e2\u81e3\u81e4\u81e5\u81e6\u81e7\u81e8\u81e9\u81ea\u81eb\u81ec\u81ed\u81ee\u81ef\u81f0\u81f1\u81f2\u81f3\u81f4\u81f5\u81f6\u81f7\u81f8\u81f9\u81fa\u81fb\u81fc\u81fd\u81fe\u81ff\u8200\u8201\u8202\u8203\u8204\u8205\u8206\u8207\u8208\u8209\u820a\u820b\u820c\u820d\u820e\u820f\u8210\u8211\u8212\u8213\u8214\u8215\u8216\u8217\u8218\u8219\u821a\u821b\u821c\u821d\u821e\u821f\u8220\u8221\u8222\u8223\u8224\u8225\u8226\u8227\u8228\u8229\u822a\u822b\u822c\u822d\u822e\u822f\u8230\u8231\u8232\u8233\u8234\u8235\u8236\u8237\u8238\u8239\u823a\u823b\u823c\u823d\u823e\u823f\u8240\u8241\u8242\u8243\u8244\u8245\u8246\u8247\u8248\u8249\u824a\u824b\u824c\u824d\u824e\u824f\u8250\u8251\u8252\u8253\u8254\u8255\u8256\u8257\u8258\u8259\u825a\u825b\u825c\u825d\u825e\u825f\u8260\u8261\u8262\u8263\u8264\u8265\u8266\u8267\u8268\u8269\u826a\u826b\u826c\u826d\u826e\u826f\u8270\u8271\u8272\u8273\u8274\u8275\u8276\u8277\u8278\u8279\u827a\u827b\u827c\u827d\u827e\u827f\u8280\u8281\u8282\u8283\u8284\u8285\u8286\u8287\u8288\u8289\u828a\u828b\u828c\u828d\u828e\u828f\u8290\u8291\u8292\u8293\u8294\u8295\u8296\u8297\u8298\u8299\u829a\u829b\u829c\u829d\u829e\u829f\u82a0\u82a1\u82a2\u82a3\u82a4\u82a5\u82a6\u82a7\u82a8\u82a9\u82aa\u82ab\u82ac\u82ad\u82ae\u82af\u82b0\u82b1\u82b2\u82b3\u82b4\u82b5\u82b6\u82b7\u82b8\u82b9\u82ba\u82bb\u82bc\u82bd\u82be\u82bf\u82c0\u82c1\u82c2\u82c3\u82c4\u82c5\u82c6\u82c7\u82c8\u82c9\u82ca\u82cb\u82cc\u82cd\u82ce\u82cf\u82d0\u82d1\u82d2\u82d3\u82d4\u82d5\u82d6\u82d7\u82d8\u82d9\u82da\u82db\u82dc\u82dd\u82de\u82df\u82e0\u82e1\u82e2\u82e3\u82e4\u82e5\u82e6\u82e7\u82e8\u82e9\u82ea\u82eb\u82ec\u82ed\u82ee\u82ef\u82f0\u82f1\u82f2\u82f3\u82f4\u82f5\u82f6\u82f7\u82f8\u82f9\u82fa\u82fb\u82fc\u82fd\u82fe\u82ff\u8300\u8301\u8302\u8303\u8304\u8305\u8306\u8307\u8308\u8309\u830a\u830b\u830c\u830d\u830e\u830f\u8310\u8311\u8312\u8313\u8314\u8315\u8316\u8317\u8318\u8319\u831a\u831b\u831c\u831d\u831e\u831f\u8320\u8321\u8322\u8323\u8324\u8325\u8326\u8327\u8328\u8329\u832a\u832b\u832c\u832d\u832e\u832f\u8330\u8331\u8332\u8333\u8334\u8335\u8336\u8337\u8338\u8339\u833a\u833b\u833c\u833d\u833e\u833f\u8340\u8341\u8342\u8343\u8344\u8345\u8346\u8347\u8348\u8349\u834a\u834b\u834c\u834d\u834e\u834f\u8350\u8351\u8352\u8353\u8354\u8355\u8356\u8357\u8358\u8359\u835a\u835b\u835c\u835d\u835e\u835f\u8360\u8361\u8362\u8363\u8364\u8365\u8366\u8367\u8368\u8369\u836a\u836b\u836c\u836d\u836e\u836f\u8370\u8371\u8372\u8373\u8374\u8375\u8376\u8377\u8378\u8379\u837a\u837b\u837c\u837d\u837e\u837f\u8380\u8381\u8382\u8383\u8384\u8385\u8386\u8387\u8388\u8389\u838a\u838b\u838c\u838d\u838e\u838f\u8390\u8391\u8392\u8393\u8394\u8395\u8396\u8397\u8398\u8399\u839a\u839b\u839c\u839d\u839e\u839f\u83a0\u83a1\u83a2\u83a3\u83a4\u83a5\u83a6\u83a7\u83a8\u83a9\u83aa\u83ab\u83ac\u83ad\u83ae\u83af\u83b0\u83b1\u83b2\u83b3\u83b4\u83b5\u83b6\u83b7\u83b8\u83b9\u83ba\u83bb\u83bc\u83bd\u83be\u83bf\u83c0\u83c1\u83c2\u83c3\u83c4\u83c5\u83c6\u83c7\u83c8\u83c9\u83ca\u83cb\u83cc\u83cd\u83ce\u83cf\u83d0\u83d1\u83d2\u83d3\u83d4\u83d5\u83d6\u83d7\u83d8\u83d9\u83da\u83db\u83dc\u83dd\u83de\u83df\u83e0\u83e1\u83e2\u83e3\u83e4\u83e5\u83e6\u83e7\u83e8\u83e9\u83ea\u83eb\u83ec\u83ed\u83ee\u83ef\u83f0\u83f1\u83f2\u83f3\u83f4\u83f5\u83f6\u83f7\u83f8\u83f9\u83fa\u83fb\u83fc\u83fd\u83fe\u83ff\u8400\u8401\u8402\u8403\u8404\u8405\u8406\u8407\u8408\u8409\u840a\u840b\u840c\u840d\u840e\u840f\u8410\u8411\u8412\u8413\u8414\u8415\u8416\u8417\u8418\u8419\u841a\u841b\u841c\u841d\u841e\u841f\u8420\u8421\u8422\u8423\u8424\u8425\u8426\u8427\u8428\u8429\u842a\u842b\u842c\u842d\u842e\u842f\u8430\u8431\u8432\u8433\u8434\u8435\u8436\u8437\u8438\u8439\u843a\u843b\u843c\u843d\u843e\u843f\u8440\u8441\u8442\u8443\u8444\u8445\u8446\u8447\u8448\u8449\u844a\u844b\u844c\u844d\u844e\u844f\u8450\u8451\u8452\u8453\u8454\u8455\u8456\u8457\u8458\u8459\u845a\u845b\u845c\u845d\u845e\u845f\u8460\u8461\u8462\u8463\u8464\u8465\u8466\u8467\u8468\u8469\u846a\u846b\u846c\u846d\u846e\u846f\u8470\u8471\u8472\u8473\u8474\u8475\u8476\u8477\u8478\u8479\u847a\u847b\u847c\u847d\u847e\u847f\u8480\u8481\u8482\u8483\u8484\u8485\u8486\u8487\u8488\u8489\u848a\u848b\u848c\u848d\u848e\u848f\u8490\u8491\u8492\u8493\u8494\u8495\u8496\u8497\u8498\u8499\u849a\u849b\u849c\u849d\u849e\u849f\u84a0\u84a1\u84a2\u84a3\u84a4\u84a5\u84a6\u84a7\u84a8\u84a9\u84aa\u84ab\u84ac\u84ad\u84ae\u84af\u84b0\u84b1\u84b2\u84b3\u84b4\u84b5\u84b6\u84b7\u84b8\u84b9\u84ba\u84bb\u84bc\u84bd\u84be\u84bf\u84c0\u84c1\u84c2\u84c3\u84c4\u84c5\u84c6\u84c7\u84c8\u84c9\u84ca\u84cb\u84cc\u84cd\u84ce\u84cf\u84d0\u84d1\u84d2\u84d3\u84d4\u84d5\u84d6\u84d7\u84d8\u84d9\u84da\u84db\u84dc\u84dd\u84de\u84df\u84e0\u84e1\u84e2\u84e3\u84e4\u84e5\u84e6\u84e7\u84e8\u84e9\u84ea\u84eb\u84ec\u84ed\u84ee\u84ef\u84f0\u84f1\u84f2\u84f3\u84f4\u84f5\u84f6\u84f7\u84f8\u84f9\u84fa\u84fb\u84fc\u84fd\u84fe\u84ff\u8500\u8501\u8502\u8503\u8504\u8505\u8506\u8507\u8508\u8509\u850a\u850b\u850c\u850d\u850e\u850f\u8510\u8511\u8512\u8513\u8514\u8515\u8516\u8517\u8518\u8519\u851a\u851b\u851c\u851d\u851e\u851f\u8520\u8521\u8522\u8523\u8524\u8525\u8526\u8527\u8528\u8529\u852a\u852b\u852c\u852d\u852e\u852f\u8530\u8531\u8532\u8533\u8534\u8535\u8536\u8537\u8538\u8539\u853a\u853b\u853c\u853d\u853e\u853f\u8540\u8541\u8542\u8543\u8544\u8545\u8546\u8547\u8548\u8549\u854a\u854b\u854c\u854d\u854e\u854f\u8550\u8551\u8552\u8553\u8554\u8555\u8556\u8557\u8558\u8559\u855a\u855b\u855c\u855d\u855e\u855f\u8560\u8561\u8562\u8563\u8564\u8565\u8566\u8567\u8568\u8569\u856a\u856b\u856c\u856d\u856e\u856f\u8570\u8571\u8572\u8573\u8574\u8575\u8576\u8577\u8578\u8579\u857a\u857b\u857c\u857d\u857e\u857f\u8580\u8581\u8582\u8583\u8584\u8585\u8586\u8587\u8588\u8589\u858a\u858b\u858c\u858d\u858e\u858f\u8590\u8591\u8592\u8593\u8594\u8595\u8596\u8597\u8598\u8599\u859a\u859b\u859c\u859d\u859e\u859f\u85a0\u85a1\u85a2\u85a3\u85a4\u85a5\u85a6\u85a7\u85a8\u85a9\u85aa\u85ab\u85ac\u85ad\u85ae\u85af\u85b0\u85b1\u85b2\u85b3\u85b4\u85b5\u85b6\u85b7\u85b8\u85b9\u85ba\u85bb\u85bc\u85bd\u85be\u85bf\u85c0\u85c1\u85c2\u85c3\u85c4\u85c5\u85c6\u85c7\u85c8\u85c9\u85ca\u85cb\u85cc\u85cd\u85ce\u85cf\u85d0\u85d1\u85d2\u85d3\u85d4\u85d5\u85d6\u85d7\u85d8\u85d9\u85da\u85db\u85dc\u85dd\u85de\u85df\u85e0\u85e1\u85e2\u85e3\u85e4\u85e5\u85e6\u85e7\u85e8\u85e9\u85ea\u85eb\u85ec\u85ed\u85ee\u85ef\u85f0\u85f1\u85f2\u85f3\u85f4\u85f5\u85f6\u85f7\u85f8\u85f9\u85fa\u85fb\u85fc\u85fd\u85fe\u85ff\u8600\u8601\u8602\u8603\u8604\u8605\u8606\u8607\u8608\u8609\u860a\u860b\u860c\u860d\u860e\u860f\u8610\u8611\u8612\u8613\u8614\u8615\u8616\u8617\u8618\u8619\u861a\u861b\u861c\u861d\u861e\u861f\u8620\u8621\u8622\u8623\u8624\u8625\u8626\u8627\u8628\u8629\u862a\u862b\u862c\u862d\u862e\u862f\u8630\u8631\u8632\u8633\u8634\u8635\u8636\u8637\u8638\u8639\u863a\u863b\u863c\u863d\u863e\u863f\u8640\u8641\u8642\u8643\u8644\u8645\u8646\u8647\u8648\u8649\u864a\u864b\u864c\u864d\u864e\u864f\u8650\u8651\u8652\u8653\u8654\u8655\u8656\u8657\u8658\u8659\u865a\u865b\u865c\u865d\u865e\u865f\u8660\u8661\u8662\u8663\u8664\u8665\u8666\u8667\u8668\u8669\u866a\u866b\u866c\u866d\u866e\u866f\u8670\u8671\u8672\u8673\u8674\u8675\u8676\u8677\u8678\u8679\u867a\u867b\u867c\u867d\u867e\u867f\u8680\u8681\u8682\u8683\u8684\u8685\u8686\u8687\u8688\u8689\u868a\u868b\u868c\u868d\u868e\u868f\u8690\u8691\u8692\u8693\u8694\u8695\u8696\u8697\u8698\u8699\u869a\u869b\u869c\u869d\u869e\u869f\u86a0\u86a1\u86a2\u86a3\u86a4\u86a5\u86a6\u86a7\u86a8\u86a9\u86aa\u86ab\u86ac\u86ad\u86ae\u86af\u86b0\u86b1\u86b2\u86b3\u86b4\u86b5\u86b6\u86b7\u86b8\u86b9\u86ba\u86bb\u86bc\u86bd\u86be\u86bf\u86c0\u86c1\u86c2\u86c3\u86c4\u86c5\u86c6\u86c7\u86c8\u86c9\u86ca\u86cb\u86cc\u86cd\u86ce\u86cf\u86d0\u86d1\u86d2\u86d3\u86d4\u86d5\u86d6\u86d7\u86d8\u86d9\u86da\u86db\u86dc\u86dd\u86de\u86df\u86e0\u86e1\u86e2\u86e3\u86e4\u86e5\u86e6\u86e7\u86e8\u86e9\u86ea\u86eb\u86ec\u86ed\u86ee\u86ef\u86f0\u86f1\u86f2\u86f3\u86f4\u86f5\u86f6\u86f7\u86f8\u86f9\u86fa\u86fb\u86fc\u86fd\u86fe\u86ff\u8700\u8701\u8702\u8703\u8704\u8705\u8706\u8707\u8708\u8709\u870a\u870b\u870c\u870d\u870e\u870f\u8710\u8711\u8712\u8713\u8714\u8715\u8716\u8717\u8718\u8719\u871a\u871b\u871c\u871d\u871e\u871f\u8720\u8721\u8722\u8723\u8724\u8725\u8726\u8727\u8728\u8729\u872a\u872b\u872c\u872d\u872e\u872f\u8730\u8731\u8732\u8733\u8734\u8735\u8736\u8737\u8738\u8739\u873a\u873b\u873c\u873d\u873e\u873f\u8740\u8741\u8742\u8743\u8744\u8745\u8746\u8747\u8748\u8749\u874a\u874b\u874c\u874d\u874e\u874f\u8750\u8751\u8752\u8753\u8754\u8755\u8756\u8757\u8758\u8759\u875a\u875b\u875c\u875d\u875e\u875f\u8760\u8761\u8762\u8763\u8764\u8765\u8766\u8767\u8768\u8769\u876a\u876b\u876c\u876d\u876e\u876f\u8770\u8771\u8772\u8773\u8774\u8775\u8776\u8777\u8778\u8779\u877a\u877b\u877c\u877d\u877e\u877f\u8780\u8781\u8782\u8783\u8784\u8785\u8786\u8787\u8788\u8789\u878a\u878b\u878c\u878d\u878e\u878f\u8790\u8791\u8792\u8793\u8794\u8795\u8796\u8797\u8798\u8799\u879a\u879b\u879c\u879d\u879e\u879f\u87a0\u87a1\u87a2\u87a3\u87a4\u87a5\u87a6\u87a7\u87a8\u87a9\u87aa\u87ab\u87ac\u87ad\u87ae\u87af\u87b0\u87b1\u87b2\u87b3\u87b4\u87b5\u87b6\u87b7\u87b8\u87b9\u87ba\u87bb\u87bc\u87bd\u87be\u87bf\u87c0\u87c1\u87c2\u87c3\u87c4\u87c5\u87c6\u87c7\u87c8\u87c9\u87ca\u87cb\u87cc\u87cd\u87ce\u87cf\u87d0\u87d1\u87d2\u87d3\u87d4\u87d5\u87d6\u87d7\u87d8\u87d9\u87da\u87db\u87dc\u87dd\u87de\u87df\u87e0\u87e1\u87e2\u87e3\u87e4\u87e5\u87e6\u87e7\u87e8\u87e9\u87ea\u87eb\u87ec\u87ed\u87ee\u87ef\u87f0\u87f1\u87f2\u87f3\u87f4\u87f5\u87f6\u87f7\u87f8\u87f9\u87fa\u87fb\u87fc\u87fd\u87fe\u87ff\u8800\u8801\u8802\u8803\u8804\u8805\u8806\u8807\u8808\u8809\u880a\u880b\u880c\u880d\u880e\u880f\u8810\u8811\u8812\u8813\u8814\u8815\u8816\u8817\u8818\u8819\u881a\u881b\u881c\u881d\u881e\u881f\u8820\u8821\u8822\u8823\u8824\u8825\u8826\u8827\u8828\u8829\u882a\u882b\u882c\u882d\u882e\u882f\u8830\u8831\u8832\u8833\u8834\u8835\u8836\u8837\u8838\u8839\u883a\u883b\u883c\u883d\u883e\u883f\u8840\u8841\u8842\u8843\u8844\u8845\u8846\u8847\u8848\u8849\u884a\u884b\u884c\u884d\u884e\u884f\u8850\u8851\u8852\u8853\u8854\u8855\u8856\u8857\u8858\u8859\u885a\u885b\u885c\u885d\u885e\u885f\u8860\u8861\u8862\u8863\u8864\u8865\u8866\u8867\u8868\u8869\u886a\u886b\u886c\u886d\u886e\u886f\u8870\u8871\u8872\u8873\u8874\u8875\u8876\u8877\u8878\u8879\u887a\u887b\u887c\u887d\u887e\u887f\u8880\u8881\u8882\u8883\u8884\u8885\u8886\u8887\u8888\u8889\u888a\u888b\u888c\u888d\u888e\u888f\u8890\u8891\u8892\u8893\u8894\u8895\u8896\u8897\u8898\u8899\u889a\u889b\u889c\u889d\u889e\u889f\u88a0\u88a1\u88a2\u88a3\u88a4\u88a5\u88a6\u88a7\u88a8\u88a9\u88aa\u88ab\u88ac\u88ad\u88ae\u88af\u88b0\u88b1\u88b2\u88b3\u88b4\u88b5\u88b6\u88b7\u88b8\u88b9\u88ba\u88bb\u88bc\u88bd\u88be\u88bf\u88c0\u88c1\u88c2\u88c3\u88c4\u88c5\u88c6\u88c7\u88c8\u88c9\u88ca\u88cb\u88cc\u88cd\u88ce\u88cf\u88d0\u88d1\u88d2\u88d3\u88d4\u88d5\u88d6\u88d7\u88d8\u88d9\u88da\u88db\u88dc\u88dd\u88de\u88df\u88e0\u88e1\u88e2\u88e3\u88e4\u88e5\u88e6\u88e7\u88e8\u88e9\u88ea\u88eb\u88ec\u88ed\u88ee\u88ef\u88f0\u88f1\u88f2\u88f3\u88f4\u88f5\u88f6\u88f7\u88f8\u88f9\u88fa\u88fb\u88fc\u88fd\u88fe\u88ff\u8900\u8901\u8902\u8903\u8904\u8905\u8906\u8907\u8908\u8909\u890a\u890b\u890c\u890d\u890e\u890f\u8910\u8911\u8912\u8913\u8914\u8915\u8916\u8917\u8918\u8919\u891a\u891b\u891c\u891d\u891e\u891f\u8920\u8921\u8922\u8923\u8924\u8925\u8926\u8927\u8928\u8929\u892a\u892b\u892c\u892d\u892e\u892f\u8930\u8931\u8932\u8933\u8934\u8935\u8936\u8937\u8938\u8939\u893a\u893b\u893c\u893d\u893e\u893f\u8940\u8941\u8942\u8943\u8944\u8945\u8946\u8947\u8948\u8949\u894a\u894b\u894c\u894d\u894e\u894f\u8950\u8951\u8952\u8953\u8954\u8955\u8956\u8957\u8958\u8959\u895a\u895b\u895c\u895d\u895e\u895f\u8960\u8961\u8962\u8963\u8964\u8965\u8966\u8967\u8968\u8969\u896a\u896b\u896c\u896d\u896e\u896f\u8970\u8971\u8972\u8973\u8974\u8975\u8976\u8977\u8978\u8979\u897a\u897b\u897c\u897d\u897e\u897f\u8980\u8981\u8982\u8983\u8984\u8985\u8986\u8987\u8988\u8989\u898a\u898b\u898c\u898d\u898e\u898f\u8990\u8991\u8992\u8993\u8994\u8995\u8996\u8997\u8998\u8999\u899a\u899b\u899c\u899d\u899e\u899f\u89a0\u89a1\u89a2\u89a3\u89a4\u89a5\u89a6\u89a7\u89a8\u89a9\u89aa\u89ab\u89ac\u89ad\u89ae\u89af\u89b0\u89b1\u89b2\u89b3\u89b4\u89b5\u89b6\u89b7\u89b8\u89b9\u89ba\u89bb\u89bc\u89bd\u89be\u89bf\u89c0\u89c1\u89c2\u89c3\u89c4\u89c5\u89c6\u89c7\u89c8\u89c9\u89ca\u89cb\u89cc\u89cd\u89ce\u89cf\u89d0\u89d1\u89d2\u89d3\u89d4\u89d5\u89d6\u89d7\u89d8\u89d9\u89da\u89db\u89dc\u89dd\u89de\u89df\u89e0\u89e1\u89e2\u89e3\u89e4\u89e5\u89e6\u89e7\u89e8\u89e9\u89ea\u89eb\u89ec\u89ed\u89ee\u89ef\u89f0\u89f1\u89f2\u89f3\u89f4\u89f5\u89f6\u89f7\u89f8\u89f9\u89fa\u89fb\u89fc\u89fd\u89fe\u89ff\u8a00\u8a01\u8a02\u8a03\u8a04\u8a05\u8a06\u8a07\u8a08\u8a09\u8a0a\u8a0b\u8a0c\u8a0d\u8a0e\u8a0f\u8a10\u8a11\u8a12\u8a13\u8a14\u8a15\u8a16\u8a17\u8a18\u8a19\u8a1a\u8a1b\u8a1c\u8a1d\u8a1e\u8a1f\u8a20\u8a21\u8a22\u8a23\u8a24\u8a25\u8a26\u8a27\u8a28\u8a29\u8a2a\u8a2b\u8a2c\u8a2d\u8a2e\u8a2f\u8a30\u8a31\u8a32\u8a33\u8a34\u8a35\u8a36\u8a37\u8a38\u8a39\u8a3a\u8a3b\u8a3c\u8a3d\u8a3e\u8a3f\u8a40\u8a41\u8a42\u8a43\u8a44\u8a45\u8a46\u8a47\u8a48\u8a49\u8a4a\u8a4b\u8a4c\u8a4d\u8a4e\u8a4f\u8a50\u8a51\u8a52\u8a53\u8a54\u8a55\u8a56\u8a57\u8a58\u8a59\u8a5a\u8a5b\u8a5c\u8a5d\u8a5e\u8a5f\u8a60\u8a61\u8a62\u8a63\u8a64\u8a65\u8a66\u8a67\u8a68\u8a69\u8a6a\u8a6b\u8a6c\u8a6d\u8a6e\u8a6f\u8a70\u8a71\u8a72\u8a73\u8a74\u8a75\u8a76\u8a77\u8a78\u8a79\u8a7a\u8a7b\u8a7c\u8a7d\u8a7e\u8a7f\u8a80\u8a81\u8a82\u8a83\u8a84\u8a85\u8a86\u8a87\u8a88\u8a89\u8a8a\u8a8b\u8a8c\u8a8d\u8a8e\u8a8f\u8a90\u8a91\u8a92\u8a93\u8a94\u8a95\u8a96\u8a97\u8a98\u8a99\u8a9a\u8a9b\u8a9c\u8a9d\u8a9e\u8a9f\u8aa0\u8aa1\u8aa2\u8aa3\u8aa4\u8aa5\u8aa6\u8aa7\u8aa8\u8aa9\u8aaa\u8aab\u8aac\u8aad\u8aae\u8aaf\u8ab0\u8ab1\u8ab2\u8ab3\u8ab4\u8ab5\u8ab6\u8ab7\u8ab8\u8ab9\u8aba\u8abb\u8abc\u8abd\u8abe\u8abf\u8ac0\u8ac1\u8ac2\u8ac3\u8ac4\u8ac5\u8ac6\u8ac7\u8ac8\u8ac9\u8aca\u8acb\u8acc\u8acd\u8ace\u8acf\u8ad0\u8ad1\u8ad2\u8ad3\u8ad4\u8ad5\u8ad6\u8ad7\u8ad8\u8ad9\u8ada\u8adb\u8adc\u8add\u8ade\u8adf\u8ae0\u8ae1\u8ae2\u8ae3\u8ae4\u8ae5\u8ae6\u8ae7\u8ae8\u8ae9\u8aea\u8aeb\u8aec\u8aed\u8aee\u8aef\u8af0\u8af1\u8af2\u8af3\u8af4\u8af5\u8af6\u8af7\u8af8\u8af9\u8afa\u8afb\u8afc\u8afd\u8afe\u8aff\u8b00\u8b01\u8b02\u8b03\u8b04\u8b05\u8b06\u8b07\u8b08\u8b09\u8b0a\u8b0b\u8b0c\u8b0d\u8b0e\u8b0f\u8b10\u8b11\u8b12\u8b13\u8b14\u8b15\u8b16\u8b17\u8b18\u8b19\u8b1a\u8b1b\u8b1c\u8b1d\u8b1e\u8b1f\u8b20\u8b21\u8b22\u8b23\u8b24\u8b25\u8b26\u8b27\u8b28\u8b29\u8b2a\u8b2b\u8b2c\u8b2d\u8b2e\u8b2f\u8b30\u8b31\u8b32\u8b33\u8b34\u8b35\u8b36\u8b37\u8b38\u8b39\u8b3a\u8b3b\u8b3c\u8b3d\u8b3e\u8b3f\u8b40\u8b41\u8b42\u8b43\u8b44\u8b45\u8b46\u8b47\u8b48\u8b49\u8b4a\u8b4b\u8b4c\u8b4d\u8b4e\u8b4f\u8b50\u8b51\u8b52\u8b53\u8b54\u8b55\u8b56\u8b57\u8b58\u8b59\u8b5a\u8b5b\u8b5c\u8b5d\u8b5e\u8b5f\u8b60\u8b61\u8b62\u8b63\u8b64\u8b65\u8b66\u8b67\u8b68\u8b69\u8b6a\u8b6b\u8b6c\u8b6d\u8b6e\u8b6f\u8b70\u8b71\u8b72\u8b73\u8b74\u8b75\u8b76\u8b77\u8b78\u8b79\u8b7a\u8b7b\u8b7c\u8b7d\u8b7e\u8b7f\u8b80\u8b81\u8b82\u8b83\u8b84\u8b85\u8b86\u8b87\u8b88\u8b89\u8b8a\u8b8b\u8b8c\u8b8d\u8b8e\u8b8f\u8b90\u8b91\u8b92\u8b93\u8b94\u8b95\u8b96\u8b97\u8b98\u8b99\u8b9a\u8b9b\u8b9c\u8b9d\u8b9e\u8b9f\u8ba0\u8ba1\u8ba2\u8ba3\u8ba4\u8ba5\u8ba6\u8ba7\u8ba8\u8ba9\u8baa\u8bab\u8bac\u8bad\u8bae\u8baf\u8bb0\u8bb1\u8bb2\u8bb3\u8bb4\u8bb5\u8bb6\u8bb7\u8bb8\u8bb9\u8bba\u8bbb\u8bbc\u8bbd\u8bbe\u8bbf\u8bc0\u8bc1\u8bc2\u8bc3\u8bc4\u8bc5\u8bc6\u8bc7\u8bc8\u8bc9\u8bca\u8bcb\u8bcc\u8bcd\u8bce\u8bcf\u8bd0\u8bd1\u8bd2\u8bd3\u8bd4\u8bd5\u8bd6\u8bd7\u8bd8\u8bd9\u8bda\u8bdb\u8bdc\u8bdd\u8bde\u8bdf\u8be0\u8be1\u8be2\u8be3\u8be4\u8be5\u8be6\u8be7\u8be8\u8be9\u8bea\u8beb\u8bec\u8bed\u8bee\u8bef\u8bf0\u8bf1\u8bf2\u8bf3\u8bf4\u8bf5\u8bf6\u8bf7\u8bf8\u8bf9\u8bfa\u8bfb\u8bfc\u8bfd\u8bfe\u8bff\u8c00\u8c01\u8c02\u8c03\u8c04\u8c05\u8c06\u8c07\u8c08\u8c09\u8c0a\u8c0b\u8c0c\u8c0d\u8c0e\u8c0f\u8c10\u8c11\u8c12\u8c13\u8c14\u8c15\u8c16\u8c17\u8c18\u8c19\u8c1a\u8c1b\u8c1c\u8c1d\u8c1e\u8c1f\u8c20\u8c21\u8c22\u8c23\u8c24\u8c25\u8c26\u8c27\u8c28\u8c29\u8c2a\u8c2b\u8c2c\u8c2d\u8c2e\u8c2f\u8c30\u8c31\u8c32\u8c33\u8c34\u8c35\u8c36\u8c37\u8c38\u8c39\u8c3a\u8c3b\u8c3c\u8c3d\u8c3e\u8c3f\u8c40\u8c41\u8c42\u8c43\u8c44\u8c45\u8c46\u8c47\u8c48\u8c49\u8c4a\u8c4b\u8c4c\u8c4d\u8c4e\u8c4f\u8c50\u8c51\u8c52\u8c53\u8c54\u8c55\u8c56\u8c57\u8c58\u8c59\u8c5a\u8c5b\u8c5c\u8c5d\u8c5e\u8c5f\u8c60\u8c61\u8c62\u8c63\u8c64\u8c65\u8c66\u8c67\u8c68\u8c69\u8c6a\u8c6b\u8c6c\u8c6d\u8c6e\u8c6f\u8c70\u8c71\u8c72\u8c73\u8c74\u8c75\u8c76\u8c77\u8c78\u8c79\u8c7a\u8c7b\u8c7c\u8c7d\u8c7e\u8c7f\u8c80\u8c81\u8c82\u8c83\u8c84\u8c85\u8c86\u8c87\u8c88\u8c89\u8c8a\u8c8b\u8c8c\u8c8d\u8c8e\u8c8f\u8c90\u8c91\u8c92\u8c93\u8c94\u8c95\u8c96\u8c97\u8c98\u8c99\u8c9a\u8c9b\u8c9c\u8c9d\u8c9e\u8c9f\u8ca0\u8ca1\u8ca2\u8ca3\u8ca4\u8ca5\u8ca6\u8ca7\u8ca8\u8ca9\u8caa\u8cab\u8cac\u8cad\u8cae\u8caf\u8cb0\u8cb1\u8cb2\u8cb3\u8cb4\u8cb5\u8cb6\u8cb7\u8cb8\u8cb9\u8cba\u8cbb\u8cbc\u8cbd\u8cbe\u8cbf\u8cc0\u8cc1\u8cc2\u8cc3\u8cc4\u8cc5\u8cc6\u8cc7\u8cc8\u8cc9\u8cca\u8ccb\u8ccc\u8ccd\u8cce\u8ccf\u8cd0\u8cd1\u8cd2\u8cd3\u8cd4\u8cd5\u8cd6\u8cd7\u8cd8\u8cd9\u8cda\u8cdb\u8cdc\u8cdd\u8cde\u8cdf\u8ce0\u8ce1\u8ce2\u8ce3\u8ce4\u8ce5\u8ce6\u8ce7\u8ce8\u8ce9\u8cea\u8ceb\u8cec\u8ced\u8cee\u8cef\u8cf0\u8cf1\u8cf2\u8cf3\u8cf4\u8cf5\u8cf6\u8cf7\u8cf8\u8cf9\u8cfa\u8cfb\u8cfc\u8cfd\u8cfe\u8cff\u8d00\u8d01\u8d02\u8d03\u8d04\u8d05\u8d06\u8d07\u8d08\u8d09\u8d0a\u8d0b\u8d0c\u8d0d\u8d0e\u8d0f\u8d10\u8d11\u8d12\u8d13\u8d14\u8d15\u8d16\u8d17\u8d18\u8d19\u8d1a\u8d1b\u8d1c\u8d1d\u8d1e\u8d1f\u8d20\u8d21\u8d22\u8d23\u8d24\u8d25\u8d26\u8d27\u8d28\u8d29\u8d2a\u8d2b\u8d2c\u8d2d\u8d2e\u8d2f\u8d30\u8d31\u8d32\u8d33\u8d34\u8d35\u8d36\u8d37\u8d38\u8d39\u8d3a\u8d3b\u8d3c\u8d3d\u8d3e\u8d3f\u8d40\u8d41\u8d42\u8d43\u8d44\u8d45\u8d46\u8d47\u8d48\u8d49\u8d4a\u8d4b\u8d4c\u8d4d\u8d4e\u8d4f\u8d50\u8d51\u8d52\u8d53\u8d54\u8d55\u8d56\u8d57\u8d58\u8d59\u8d5a\u8d5b\u8d5c\u8d5d\u8d5e\u8d5f\u8d60\u8d61\u8d62\u8d63\u8d64\u8d65\u8d66\u8d67\u8d68\u8d69\u8d6a\u8d6b\u8d6c\u8d6d\u8d6e\u8d6f\u8d70\u8d71\u8d72\u8d73\u8d74\u8d75\u8d76\u8d77\u8d78\u8d79\u8d7a\u8d7b\u8d7c\u8d7d\u8d7e\u8d7f\u8d80\u8d81\u8d82\u8d83\u8d84\u8d85\u8d86\u8d87\u8d88\u8d89\u8d8a\u8d8b\u8d8c\u8d8d\u8d8e\u8d8f\u8d90\u8d91\u8d92\u8d93\u8d94\u8d95\u8d96\u8d97\u8d98\u8d99\u8d9a\u8d9b\u8d9c\u8d9d\u8d9e\u8d9f\u8da0\u8da1\u8da2\u8da3\u8da4\u8da5\u8da6\u8da7\u8da8\u8da9\u8daa\u8dab\u8dac\u8dad\u8dae\u8daf\u8db0\u8db1\u8db2\u8db3\u8db4\u8db5\u8db6\u8db7\u8db8\u8db9\u8dba\u8dbb\u8dbc\u8dbd\u8dbe\u8dbf\u8dc0\u8dc1\u8dc2\u8dc3\u8dc4\u8dc5\u8dc6\u8dc7\u8dc8\u8dc9\u8dca\u8dcb\u8dcc\u8dcd\u8dce\u8dcf\u8dd0\u8dd1\u8dd2\u8dd3\u8dd4\u8dd5\u8dd6\u8dd7\u8dd8\u8dd9\u8dda\u8ddb\u8ddc\u8ddd\u8dde\u8ddf\u8de0\u8de1\u8de2\u8de3\u8de4\u8de5\u8de6\u8de7\u8de8\u8de9\u8dea\u8deb\u8dec\u8ded\u8dee\u8def\u8df0\u8df1\u8df2\u8df3\u8df4\u8df5\u8df6\u8df7\u8df8\u8df9\u8dfa\u8dfb\u8dfc\u8dfd\u8dfe\u8dff\u8e00\u8e01\u8e02\u8e03\u8e04\u8e05\u8e06\u8e07\u8e08\u8e09\u8e0a\u8e0b\u8e0c\u8e0d\u8e0e\u8e0f\u8e10\u8e11\u8e12\u8e13\u8e14\u8e15\u8e16\u8e17\u8e18\u8e19\u8e1a\u8e1b\u8e1c\u8e1d\u8e1e\u8e1f\u8e20\u8e21\u8e22\u8e23\u8e24\u8e25\u8e26\u8e27\u8e28\u8e29\u8e2a\u8e2b\u8e2c\u8e2d\u8e2e\u8e2f\u8e30\u8e31\u8e32\u8e33\u8e34\u8e35\u8e36\u8e37\u8e38\u8e39\u8e3a\u8e3b\u8e3c\u8e3d\u8e3e\u8e3f\u8e40\u8e41\u8e42\u8e43\u8e44\u8e45\u8e46\u8e47\u8e48\u8e49\u8e4a\u8e4b\u8e4c\u8e4d\u8e4e\u8e4f\u8e50\u8e51\u8e52\u8e53\u8e54\u8e55\u8e56\u8e57\u8e58\u8e59\u8e5a\u8e5b\u8e5c\u8e5d\u8e5e\u8e5f\u8e60\u8e61\u8e62\u8e63\u8e64\u8e65\u8e66\u8e67\u8e68\u8e69\u8e6a\u8e6b\u8e6c\u8e6d\u8e6e\u8e6f\u8e70\u8e71\u8e72\u8e73\u8e74\u8e75\u8e76\u8e77\u8e78\u8e79\u8e7a\u8e7b\u8e7c\u8e7d\u8e7e\u8e7f\u8e80\u8e81\u8e82\u8e83\u8e84\u8e85\u8e86\u8e87\u8e88\u8e89\u8e8a\u8e8b\u8e8c\u8e8d\u8e8e\u8e8f\u8e90\u8e91\u8e92\u8e93\u8e94\u8e95\u8e96\u8e97\u8e98\u8e99\u8e9a\u8e9b\u8e9c\u8e9d\u8e9e\u8e9f\u8ea0\u8ea1\u8ea2\u8ea3\u8ea4\u8ea5\u8ea6\u8ea7\u8ea8\u8ea9\u8eaa\u8eab\u8eac\u8ead\u8eae\u8eaf\u8eb0\u8eb1\u8eb2\u8eb3\u8eb4\u8eb5\u8eb6\u8eb7\u8eb8\u8eb9\u8eba\u8ebb\u8ebc\u8ebd\u8ebe\u8ebf\u8ec0\u8ec1\u8ec2\u8ec3\u8ec4\u8ec5\u8ec6\u8ec7\u8ec8\u8ec9\u8eca\u8ecb\u8ecc\u8ecd\u8ece\u8ecf\u8ed0\u8ed1\u8ed2\u8ed3\u8ed4\u8ed5\u8ed6\u8ed7\u8ed8\u8ed9\u8eda\u8edb\u8edc\u8edd\u8ede\u8edf\u8ee0\u8ee1\u8ee2\u8ee3\u8ee4\u8ee5\u8ee6\u8ee7\u8ee8\u8ee9\u8eea\u8eeb\u8eec\u8eed\u8eee\u8eef\u8ef0\u8ef1\u8ef2\u8ef3\u8ef4\u8ef5\u8ef6\u8ef7\u8ef8\u8ef9\u8efa\u8efb\u8efc\u8efd\u8efe\u8eff\u8f00\u8f01\u8f02\u8f03\u8f04\u8f05\u8f06\u8f07\u8f08\u8f09\u8f0a\u8f0b\u8f0c\u8f0d\u8f0e\u8f0f\u8f10\u8f11\u8f12\u8f13\u8f14\u8f15\u8f16\u8f17\u8f18\u8f19\u8f1a\u8f1b\u8f1c\u8f1d\u8f1e\u8f1f\u8f20\u8f21\u8f22\u8f23\u8f24\u8f25\u8f26\u8f27\u8f28\u8f29\u8f2a\u8f2b\u8f2c\u8f2d\u8f2e\u8f2f\u8f30\u8f31\u8f32\u8f33\u8f34\u8f35\u8f36\u8f37\u8f38\u8f39\u8f3a\u8f3b\u8f3c\u8f3d\u8f3e\u8f3f\u8f40\u8f41\u8f42\u8f43\u8f44\u8f45\u8f46\u8f47\u8f48\u8f49\u8f4a\u8f4b\u8f4c\u8f4d\u8f4e\u8f4f\u8f50\u8f51\u8f52\u8f53\u8f54\u8f55\u8f56\u8f57\u8f58\u8f59\u8f5a\u8f5b\u8f5c\u8f5d\u8f5e\u8f5f\u8f60\u8f61\u8f62\u8f63\u8f64\u8f65\u8f66\u8f67\u8f68\u8f69\u8f6a\u8f6b\u8f6c\u8f6d\u8f6e\u8f6f\u8f70\u8f71\u8f72\u8f73\u8f74\u8f75\u8f76\u8f77\u8f78\u8f79\u8f7a\u8f7b\u8f7c\u8f7d\u8f7e\u8f7f\u8f80\u8f81\u8f82\u8f83\u8f84\u8f85\u8f86\u8f87\u8f88\u8f89\u8f8a\u8f8b\u8f8c\u8f8d\u8f8e\u8f8f\u8f90\u8f91\u8f92\u8f93\u8f94\u8f95\u8f96\u8f97\u8f98\u8f99\u8f9a\u8f9b\u8f9c\u8f9d\u8f9e\u8f9f\u8fa0\u8fa1\u8fa2\u8fa3\u8fa4\u8fa5\u8fa6\u8fa7\u8fa8\u8fa9\u8faa\u8fab\u8fac\u8fad\u8fae\u8faf\u8fb0\u8fb1\u8fb2\u8fb3\u8fb4\u8fb5\u8fb6\u8fb7\u8fb8\u8fb9\u8fba\u8fbb\u8fbc\u8fbd\u8fbe\u8fbf\u8fc0\u8fc1\u8fc2\u8fc3\u8fc4\u8fc5\u8fc6\u8fc7\u8fc8\u8fc9\u8fca\u8fcb\u8fcc\u8fcd\u8fce\u8fcf\u8fd0\u8fd1\u8fd2\u8fd3\u8fd4\u8fd5\u8fd6\u8fd7\u8fd8\u8fd9\u8fda\u8fdb\u8fdc\u8fdd\u8fde\u8fdf\u8fe0\u8fe1\u8fe2\u8fe3\u8fe4\u8fe5\u8fe6\u8fe7\u8fe8\u8fe9\u8fea\u8feb\u8fec\u8fed\u8fee\u8fef\u8ff0\u8ff1\u8ff2\u8ff3\u8ff4\u8ff5\u8ff6\u8ff7\u8ff8\u8ff9\u8ffa\u8ffb\u8ffc\u8ffd\u8ffe\u8fff\u9000\u9001\u9002\u9003\u9004\u9005\u9006\u9007\u9008\u9009\u900a\u900b\u900c\u900d\u900e\u900f\u9010\u9011\u9012\u9013\u9014\u9015\u9016\u9017\u9018\u9019\u901a\u901b\u901c\u901d\u901e\u901f\u9020\u9021\u9022\u9023\u9024\u9025\u9026\u9027\u9028\u9029\u902a\u902b\u902c\u902d\u902e\u902f\u9030\u9031\u9032\u9033\u9034\u9035\u9036\u9037\u9038\u9039\u903a\u903b\u903c\u903d\u903e\u903f\u9040\u9041\u9042\u9043\u9044\u9045\u9046\u9047\u9048\u9049\u904a\u904b\u904c\u904d\u904e\u904f\u9050\u9051\u9052\u9053\u9054\u9055\u9056\u9057\u9058\u9059\u905a\u905b\u905c\u905d\u905e\u905f\u9060\u9061\u9062\u9063\u9064\u9065\u9066\u9067\u9068\u9069\u906a\u906b\u906c\u906d\u906e\u906f\u9070\u9071\u9072\u9073\u9074\u9075\u9076\u9077\u9078\u9079\u907a\u907b\u907c\u907d\u907e\u907f\u9080\u9081\u9082\u9083\u9084\u9085\u9086\u9087\u9088\u9089\u908a\u908b\u908c\u908d\u908e\u908f\u9090\u9091\u9092\u9093\u9094\u9095\u9096\u9097\u9098\u9099\u909a\u909b\u909c\u909d\u909e\u909f\u90a0\u90a1\u90a2\u90a3\u90a4\u90a5\u90a6\u90a7\u90a8\u90a9\u90aa\u90ab\u90ac\u90ad\u90ae\u90af\u90b0\u90b1\u90b2\u90b3\u90b4\u90b5\u90b6\u90b7\u90b8\u90b9\u90ba\u90bb\u90bc\u90bd\u90be\u90bf\u90c0\u90c1\u90c2\u90c3\u90c4\u90c5\u90c6\u90c7\u90c8\u90c9\u90ca\u90cb\u90cc\u90cd\u90ce\u90cf\u90d0\u90d1\u90d2\u90d3\u90d4\u90d5\u90d6\u90d7\u90d8\u90d9\u90da\u90db\u90dc\u90dd\u90de\u90df\u90e0\u90e1\u90e2\u90e3\u90e4\u90e5\u90e6\u90e7\u90e8\u90e9\u90ea\u90eb\u90ec\u90ed\u90ee\u90ef\u90f0\u90f1\u90f2\u90f3\u90f4\u90f5\u90f6\u90f7\u90f8\u90f9\u90fa\u90fb\u90fc\u90fd\u90fe\u90ff\u9100\u9101\u9102\u9103\u9104\u9105\u9106\u9107\u9108\u9109\u910a\u910b\u910c\u910d\u910e\u910f\u9110\u9111\u9112\u9113\u9114\u9115\u9116\u9117\u9118\u9119\u911a\u911b\u911c\u911d\u911e\u911f\u9120\u9121\u9122\u9123\u9124\u9125\u9126\u9127\u9128\u9129\u912a\u912b\u912c\u912d\u912e\u912f\u9130\u9131\u9132\u9133\u9134\u9135\u9136\u9137\u9138\u9139\u913a\u913b\u913c\u913d\u913e\u913f\u9140\u9141\u9142\u9143\u9144\u9145\u9146\u9147\u9148\u9149\u914a\u914b\u914c\u914d\u914e\u914f\u9150\u9151\u9152\u9153\u9154\u9155\u9156\u9157\u9158\u9159\u915a\u915b\u915c\u915d\u915e\u915f\u9160\u9161\u9162\u9163\u9164\u9165\u9166\u9167\u9168\u9169\u916a\u916b\u916c\u916d\u916e\u916f\u9170\u9171\u9172\u9173\u9174\u9175\u9176\u9177\u9178\u9179\u917a\u917b\u917c\u917d\u917e\u917f\u9180\u9181\u9182\u9183\u9184\u9185\u9186\u9187\u9188\u9189\u918a\u918b\u918c\u918d\u918e\u918f\u9190\u9191\u9192\u9193\u9194\u9195\u9196\u9197\u9198\u9199\u919a\u919b\u919c\u919d\u919e\u919f\u91a0\u91a1\u91a2\u91a3\u91a4\u91a5\u91a6\u91a7\u91a8\u91a9\u91aa\u91ab\u91ac\u91ad\u91ae\u91af\u91b0\u91b1\u91b2\u91b3\u91b4\u91b5\u91b6\u91b7\u91b8\u91b9\u91ba\u91bb\u91bc\u91bd\u91be\u91bf\u91c0\u91c1\u91c2\u91c3\u91c4\u91c5\u91c6\u91c7\u91c8\u91c9\u91ca\u91cb\u91cc\u91cd\u91ce\u91cf\u91d0\u91d1\u91d2\u91d3\u91d4\u91d5\u91d6\u91d7\u91d8\u91d9\u91da\u91db\u91dc\u91dd\u91de\u91df\u91e0\u91e1\u91e2\u91e3\u91e4\u91e5\u91e6\u91e7\u91e8\u91e9\u91ea\u91eb\u91ec\u91ed\u91ee\u91ef\u91f0\u91f1\u91f2\u91f3\u91f4\u91f5\u91f6\u91f7\u91f8\u91f9\u91fa\u91fb\u91fc\u91fd\u91fe\u91ff\u9200\u9201\u9202\u9203\u9204\u9205\u9206\u9207\u9208\u9209\u920a\u920b\u920c\u920d\u920e\u920f\u9210\u9211\u9212\u9213\u9214\u9215\u9216\u9217\u9218\u9219\u921a\u921b\u921c\u921d\u921e\u921f\u9220\u9221\u9222\u9223\u9224\u9225\u9226\u9227\u9228\u9229\u922a\u922b\u922c\u922d\u922e\u922f\u9230\u9231\u9232\u9233\u9234\u9235\u9236\u9237\u9238\u9239\u923a\u923b\u923c\u923d\u923e\u923f\u9240\u9241\u9242\u9243\u9244\u9245\u9246\u9247\u9248\u9249\u924a\u924b\u924c\u924d\u924e\u924f\u9250\u9251\u9252\u9253\u9254\u9255\u9256\u9257\u9258\u9259\u925a\u925b\u925c\u925d\u925e\u925f\u9260\u9261\u9262\u9263\u9264\u9265\u9266\u9267\u9268\u9269\u926a\u926b\u926c\u926d\u926e\u926f\u9270\u9271\u9272\u9273\u9274\u9275\u9276\u9277\u9278\u9279\u927a\u927b\u927c\u927d\u927e\u927f\u9280\u9281\u9282\u9283\u9284\u9285\u9286\u9287\u9288\u9289\u928a\u928b\u928c\u928d\u928e\u928f\u9290\u9291\u9292\u9293\u9294\u9295\u9296\u9297\u9298\u9299\u929a\u929b\u929c\u929d\u929e\u929f\u92a0\u92a1\u92a2\u92a3\u92a4\u92a5\u92a6\u92a7\u92a8\u92a9\u92aa\u92ab\u92ac\u92ad\u92ae\u92af\u92b0\u92b1\u92b2\u92b3\u92b4\u92b5\u92b6\u92b7\u92b8\u92b9\u92ba\u92bb\u92bc\u92bd\u92be\u92bf\u92c0\u92c1\u92c2\u92c3\u92c4\u92c5\u92c6\u92c7\u92c8\u92c9\u92ca\u92cb\u92cc\u92cd\u92ce\u92cf\u92d0\u92d1\u92d2\u92d3\u92d4\u92d5\u92d6\u92d7\u92d8\u92d9\u92da\u92db\u92dc\u92dd\u92de\u92df\u92e0\u92e1\u92e2\u92e3\u92e4\u92e5\u92e6\u92e7\u92e8\u92e9\u92ea\u92eb\u92ec\u92ed\u92ee\u92ef\u92f0\u92f1\u92f2\u92f3\u92f4\u92f5\u92f6\u92f7\u92f8\u92f9\u92fa\u92fb\u92fc\u92fd\u92fe\u92ff\u9300\u9301\u9302\u9303\u9304\u9305\u9306\u9307\u9308\u9309\u930a\u930b\u930c\u930d\u930e\u930f\u9310\u9311\u9312\u9313\u9314\u9315\u9316\u9317\u9318\u9319\u931a\u931b\u931c\u931d\u931e\u931f\u9320\u9321\u9322\u9323\u9324\u9325\u9326\u9327\u9328\u9329\u932a\u932b\u932c\u932d\u932e\u932f\u9330\u9331\u9332\u9333\u9334\u9335\u9336\u9337\u9338\u9339\u933a\u933b\u933c\u933d\u933e\u933f\u9340\u9341\u9342\u9343\u9344\u9345\u9346\u9347\u9348\u9349\u934a\u934b\u934c\u934d\u934e\u934f\u9350\u9351\u9352\u9353\u9354\u9355\u9356\u9357\u9358\u9359\u935a\u935b\u935c\u935d\u935e\u935f\u9360\u9361\u9362\u9363\u9364\u9365\u9366\u9367\u9368\u9369\u936a\u936b\u936c\u936d\u936e\u936f\u9370\u9371\u9372\u9373\u9374\u9375\u9376\u9377\u9378\u9379\u937a\u937b\u937c\u937d\u937e\u937f\u9380\u9381\u9382\u9383\u9384\u9385\u9386\u9387\u9388\u9389\u938a\u938b\u938c\u938d\u938e\u938f\u9390\u9391\u9392\u9393\u9394\u9395\u9396\u9397\u9398\u9399\u939a\u939b\u939c\u939d\u939e\u939f\u93a0\u93a1\u93a2\u93a3\u93a4\u93a5\u93a6\u93a7\u93a8\u93a9\u93aa\u93ab\u93ac\u93ad\u93ae\u93af\u93b0\u93b1\u93b2\u93b3\u93b4\u93b5\u93b6\u93b7\u93b8\u93b9\u93ba\u93bb\u93bc\u93bd\u93be\u93bf\u93c0\u93c1\u93c2\u93c3\u93c4\u93c5\u93c6\u93c7\u93c8\u93c9\u93ca\u93cb\u93cc\u93cd\u93ce\u93cf\u93d0\u93d1\u93d2\u93d3\u93d4\u93d5\u93d6\u93d7\u93d8\u93d9\u93da\u93db\u93dc\u93dd\u93de\u93df\u93e0\u93e1\u93e2\u93e3\u93e4\u93e5\u93e6\u93e7\u93e8\u93e9\u93ea\u93eb\u93ec\u93ed\u93ee\u93ef\u93f0\u93f1\u93f2\u93f3\u93f4\u93f5\u93f6\u93f7\u93f8\u93f9\u93fa\u93fb\u93fc\u93fd\u93fe\u93ff\u9400\u9401\u9402\u9403\u9404\u9405\u9406\u9407\u9408\u9409\u940a\u940b\u940c\u940d\u940e\u940f\u9410\u9411\u9412\u9413\u9414\u9415\u9416\u9417\u9418\u9419\u941a\u941b\u941c\u941d\u941e\u941f\u9420\u9421\u9422\u9423\u9424\u9425\u9426\u9427\u9428\u9429\u942a\u942b\u942c\u942d\u942e\u942f\u9430\u9431\u9432\u9433\u9434\u9435\u9436\u9437\u9438\u9439\u943a\u943b\u943c\u943d\u943e\u943f\u9440\u9441\u9442\u9443\u9444\u9445\u9446\u9447\u9448\u9449\u944a\u944b\u944c\u944d\u944e\u944f\u9450\u9451\u9452\u9453\u9454\u9455\u9456\u9457\u9458\u9459\u945a\u945b\u945c\u945d\u945e\u945f\u9460\u9461\u9462\u9463\u9464\u9465\u9466\u9467\u9468\u9469\u946a\u946b\u946c\u946d\u946e\u946f\u9470\u9471\u9472\u9473\u9474\u9475\u9476\u9477\u9478\u9479\u947a\u947b\u947c\u947d\u947e\u947f\u9480\u9481\u9482\u9483\u9484\u9485\u9486\u9487\u9488\u9489\u948a\u948b\u948c\u948d\u948e\u948f\u9490\u9491\u9492\u9493\u9494\u9495\u9496\u9497\u9498\u9499\u949a\u949b\u949c\u949d\u949e\u949f\u94a0\u94a1\u94a2\u94a3\u94a4\u94a5\u94a6\u94a7\u94a8\u94a9\u94aa\u94ab\u94ac\u94ad\u94ae\u94af\u94b0\u94b1\u94b2\u94b3\u94b4\u94b5\u94b6\u94b7\u94b8\u94b9\u94ba\u94bb\u94bc\u94bd\u94be\u94bf\u94c0\u94c1\u94c2\u94c3\u94c4\u94c5\u94c6\u94c7\u94c8\u94c9\u94ca\u94cb\u94cc\u94cd\u94ce\u94cf\u94d0\u94d1\u94d2\u94d3\u94d4\u94d5\u94d6\u94d7\u94d8\u94d9\u94da\u94db\u94dc\u94dd\u94de\u94df\u94e0\u94e1\u94e2\u94e3\u94e4\u94e5\u94e6\u94e7\u94e8\u94e9\u94ea\u94eb\u94ec\u94ed\u94ee\u94ef\u94f0\u94f1\u94f2\u94f3\u94f4\u94f5\u94f6\u94f7\u94f8\u94f9\u94fa\u94fb\u94fc\u94fd\u94fe\u94ff\u9500\u9501\u9502\u9503\u9504\u9505\u9506\u9507\u9508\u9509\u950a\u950b\u950c\u950d\u950e\u950f\u9510\u9511\u9512\u9513\u9514\u9515\u9516\u9517\u9518\u9519\u951a\u951b\u951c\u951d\u951e\u951f\u9520\u9521\u9522\u9523\u9524\u9525\u9526\u9527\u9528\u9529\u952a\u952b\u952c\u952d\u952e\u952f\u9530\u9531\u9532\u9533\u9534\u9535\u9536\u9537\u9538\u9539\u953a\u953b\u953c\u953d\u953e\u953f\u9540\u9541\u9542\u9543\u9544\u9545\u9546\u9547\u9548\u9549\u954a\u954b\u954c\u954d\u954e\u954f\u9550\u9551\u9552\u9553\u9554\u9555\u9556\u9557\u9558\u9559\u955a\u955b\u955c\u955d\u955e\u955f\u9560\u9561\u9562\u9563\u9564\u9565\u9566\u9567\u9568\u9569\u956a\u956b\u956c\u956d\u956e\u956f\u9570\u9571\u9572\u9573\u9574\u9575\u9576\u9577\u9578\u9579\u957a\u957b\u957c\u957d\u957e\u957f\u9580\u9581\u9582\u9583\u9584\u9585\u9586\u9587\u9588\u9589\u958a\u958b\u958c\u958d\u958e\u958f\u9590\u9591\u9592\u9593\u9594\u9595\u9596\u9597\u9598\u9599\u959a\u959b\u959c\u959d\u959e\u959f\u95a0\u95a1\u95a2\u95a3\u95a4\u95a5\u95a6\u95a7\u95a8\u95a9\u95aa\u95ab\u95ac\u95ad\u95ae\u95af\u95b0\u95b1\u95b2\u95b3\u95b4\u95b5\u95b6\u95b7\u95b8\u95b9\u95ba\u95bb\u95bc\u95bd\u95be\u95bf\u95c0\u95c1\u95c2\u95c3\u95c4\u95c5\u95c6\u95c7\u95c8\u95c9\u95ca\u95cb\u95cc\u95cd\u95ce\u95cf\u95d0\u95d1\u95d2\u95d3\u95d4\u95d5\u95d6\u95d7\u95d8\u95d9\u95da\u95db\u95dc\u95dd\u95de\u95df\u95e0\u95e1\u95e2\u95e3\u95e4\u95e5\u95e6\u95e7\u95e8\u95e9\u95ea\u95eb\u95ec\u95ed\u95ee\u95ef\u95f0\u95f1\u95f2\u95f3\u95f4\u95f5\u95f6\u95f7\u95f8\u95f9\u95fa\u95fb\u95fc\u95fd\u95fe\u95ff\u9600\u9601\u9602\u9603\u9604\u9605\u9606\u9607\u9608\u9609\u960a\u960b\u960c\u960d\u960e\u960f\u9610\u9611\u9612\u9613\u9614\u9615\u9616\u9617\u9618\u9619\u961a\u961b\u961c\u961d\u961e\u961f\u9620\u9621\u9622\u9623\u9624\u9625\u9626\u9627\u9628\u9629\u962a\u962b\u962c\u962d\u962e\u962f\u9630\u9631\u9632\u9633\u9634\u9635\u9636\u9637\u9638\u9639\u963a\u963b\u963c\u963d\u963e\u963f\u9640\u9641\u9642\u9643\u9644\u9645\u9646\u9647\u9648\u9649\u964a\u964b\u964c\u964d\u964e\u964f\u9650\u9651\u9652\u9653\u9654\u9655\u9656\u9657\u9658\u9659\u965a\u965b\u965c\u965d\u965e\u965f\u9660\u9661\u9662\u9663\u9664\u9665\u9666\u9667\u9668\u9669\u966a\u966b\u966c\u966d\u966e\u966f\u9670\u9671\u9672\u9673\u9674\u9675\u9676\u9677\u9678\u9679\u967a\u967b\u967c\u967d\u967e\u967f\u9680\u9681\u9682\u9683\u9684\u9685\u9686\u9687\u9688\u9689\u968a\u968b\u968c\u968d\u968e\u968f\u9690\u9691\u9692\u9693\u9694\u9695\u9696\u9697\u9698\u9699\u969a\u969b\u969c\u969d\u969e\u969f\u96a0\u96a1\u96a2\u96a3\u96a4\u96a5\u96a6\u96a7\u96a8\u96a9\u96aa\u96ab\u96ac\u96ad\u96ae\u96af\u96b0\u96b1\u96b2\u96b3\u96b4\u96b5\u96b6\u96b7\u96b8\u96b9\u96ba\u96bb\u96bc\u96bd\u96be\u96bf\u96c0\u96c1\u96c2\u96c3\u96c4\u96c5\u96c6\u96c7\u96c8\u96c9\u96ca\u96cb\u96cc\u96cd\u96ce\u96cf\u96d0\u96d1\u96d2\u96d3\u96d4\u96d5\u96d6\u96d7\u96d8\u96d9\u96da\u96db\u96dc\u96dd\u96de\u96df\u96e0\u96e1\u96e2\u96e3\u96e4\u96e5\u96e6\u96e7\u96e8\u96e9\u96ea\u96eb\u96ec\u96ed\u96ee\u96ef\u96f0\u96f1\u96f2\u96f3\u96f4\u96f5\u96f6\u96f7\u96f8\u96f9\u96fa\u96fb\u96fc\u96fd\u96fe\u96ff\u9700\u9701\u9702\u9703\u9704\u9705\u9706\u9707\u9708\u9709\u970a\u970b\u970c\u970d\u970e\u970f\u9710\u9711\u9712\u9713\u9714\u9715\u9716\u9717\u9718\u9719\u971a\u971b\u971c\u971d\u971e\u971f\u9720\u9721\u9722\u9723\u9724\u9725\u9726\u9727\u9728\u9729\u972a\u972b\u972c\u972d\u972e\u972f\u9730\u9731\u9732\u9733\u9734\u9735\u9736\u9737\u9738\u9739\u973a\u973b\u973c\u973d\u973e\u973f\u9740\u9741\u9742\u9743\u9744\u9745\u9746\u9747\u9748\u9749\u974a\u974b\u974c\u974d\u974e\u974f\u9750\u9751\u9752\u9753\u9754\u9755\u9756\u9757\u9758\u9759\u975a\u975b\u975c\u975d\u975e\u975f\u9760\u9761\u9762\u9763\u9764\u9765\u9766\u9767\u9768\u9769\u976a\u976b\u976c\u976d\u976e\u976f\u9770\u9771\u9772\u9773\u9774\u9775\u9776\u9777\u9778\u9779\u977a\u977b\u977c\u977d\u977e\u977f\u9780\u9781\u9782\u9783\u9784\u9785\u9786\u9787\u9788\u9789\u978a\u978b\u978c\u978d\u978e\u978f\u9790\u9791\u9792\u9793\u9794\u9795\u9796\u9797\u9798\u9799\u979a\u979b\u979c\u979d\u979e\u979f\u97a0\u97a1\u97a2\u97a3\u97a4\u97a5\u97a6\u97a7\u97a8\u97a9\u97aa\u97ab\u97ac\u97ad\u97ae\u97af\u97b0\u97b1\u97b2\u97b3\u97b4\u97b5\u97b6\u97b7\u97b8\u97b9\u97ba\u97bb\u97bc\u97bd\u97be\u97bf\u97c0\u97c1\u97c2\u97c3\u97c4\u97c5\u97c6\u97c7\u97c8\u97c9\u97ca\u97cb\u97cc\u97cd\u97ce\u97cf\u97d0\u97d1\u97d2\u97d3\u97d4\u97d5\u97d6\u97d7\u97d8\u97d9\u97da\u97db\u97dc\u97dd\u97de\u97df\u97e0\u97e1\u97e2\u97e3\u97e4\u97e5\u97e6\u97e7\u97e8\u97e9\u97ea\u97eb\u97ec\u97ed\u97ee\u97ef\u97f0\u97f1\u97f2\u97f3\u97f4\u97f5\u97f6\u97f7\u97f8\u97f9\u97fa\u97fb\u97fc\u97fd\u97fe\u97ff\u9800\u9801\u9802\u9803\u9804\u9805\u9806\u9807\u9808\u9809\u980a\u980b\u980c\u980d\u980e\u980f\u9810\u9811\u9812\u9813\u9814\u9815\u9816\u9817\u9818\u9819\u981a\u981b\u981c\u981d\u981e\u981f\u9820\u9821\u9822\u9823\u9824\u9825\u9826\u9827\u9828\u9829\u982a\u982b\u982c\u982d\u982e\u982f\u9830\u9831\u9832\u9833\u9834\u9835\u9836\u9837\u9838\u9839\u983a\u983b\u983c\u983d\u983e\u983f\u9840\u9841\u9842\u9843\u9844\u9845\u9846\u9847\u9848\u9849\u984a\u984b\u984c\u984d\u984e\u984f\u9850\u9851\u9852\u9853\u9854\u9855\u9856\u9857\u9858\u9859\u985a\u985b\u985c\u985d\u985e\u985f\u9860\u9861\u9862\u9863\u9864\u9865\u9866\u9867\u9868\u9869\u986a\u986b\u986c\u986d\u986e\u986f\u9870\u9871\u9872\u9873\u9874\u9875\u9876\u9877\u9878\u9879\u987a\u987b\u987c\u987d\u987e\u987f\u9880\u9881\u9882\u9883\u9884\u9885\u9886\u9887\u9888\u9889\u988a\u988b\u988c\u988d\u988e\u988f\u9890\u9891\u9892\u9893\u9894\u9895\u9896\u9897\u9898\u9899\u989a\u989b\u989c\u989d\u989e\u989f\u98a0\u98a1\u98a2\u98a3\u98a4\u98a5\u98a6\u98a7\u98a8\u98a9\u98aa\u98ab\u98ac\u98ad\u98ae\u98af\u98b0\u98b1\u98b2\u98b3\u98b4\u98b5\u98b6\u98b7\u98b8\u98b9\u98ba\u98bb\u98bc\u98bd\u98be\u98bf\u98c0\u98c1\u98c2\u98c3\u98c4\u98c5\u98c6\u98c7\u98c8\u98c9\u98ca\u98cb\u98cc\u98cd\u98ce\u98cf\u98d0\u98d1\u98d2\u98d3\u98d4\u98d5\u98d6\u98d7\u98d8\u98d9\u98da\u98db\u98dc\u98dd\u98de\u98df\u98e0\u98e1\u98e2\u98e3\u98e4\u98e5\u98e6\u98e7\u98e8\u98e9\u98ea\u98eb\u98ec\u98ed\u98ee\u98ef\u98f0\u98f1\u98f2\u98f3\u98f4\u98f5\u98f6\u98f7\u98f8\u98f9\u98fa\u98fb\u98fc\u98fd\u98fe\u98ff\u9900\u9901\u9902\u9903\u9904\u9905\u9906\u9907\u9908\u9909\u990a\u990b\u990c\u990d\u990e\u990f\u9910\u9911\u9912\u9913\u9914\u9915\u9916\u9917\u9918\u9919\u991a\u991b\u991c\u991d\u991e\u991f\u9920\u9921\u9922\u9923\u9924\u9925\u9926\u9927\u9928\u9929\u992a\u992b\u992c\u992d\u992e\u992f\u9930\u9931\u9932\u9933\u9934\u9935\u9936\u9937\u9938\u9939\u993a\u993b\u993c\u993d\u993e\u993f\u9940\u9941\u9942\u9943\u9944\u9945\u9946\u9947\u9948\u9949\u994a\u994b\u994c\u994d\u994e\u994f\u9950\u9951\u9952\u9953\u9954\u9955\u9956\u9957\u9958\u9959\u995a\u995b\u995c\u995d\u995e\u995f\u9960\u9961\u9962\u9963\u9964\u9965\u9966\u9967\u9968\u9969\u996a\u996b\u996c\u996d\u996e\u996f\u9970\u9971\u9972\u9973\u9974\u9975\u9976\u9977\u9978\u9979\u997a\u997b\u997c\u997d\u997e\u997f\u9980\u9981\u9982\u9983\u9984\u9985\u9986\u9987\u9988\u9989\u998a\u998b\u998c\u998d\u998e\u998f\u9990\u9991\u9992\u9993\u9994\u9995\u9996\u9997\u9998\u9999\u999a\u999b\u999c\u999d\u999e\u999f\u99a0\u99a1\u99a2\u99a3\u99a4\u99a5\u99a6\u99a7\u99a8\u99a9\u99aa\u99ab\u99ac\u99ad\u99ae\u99af\u99b0\u99b1\u99b2\u99b3\u99b4\u99b5\u99b6\u99b7\u99b8\u99b9\u99ba\u99bb\u99bc\u99bd\u99be\u99bf\u99c0\u99c1\u99c2\u99c3\u99c4\u99c5\u99c6\u99c7\u99c8\u99c9\u99ca\u99cb\u99cc\u99cd\u99ce\u99cf\u99d0\u99d1\u99d2\u99d3\u99d4\u99d5\u99d6\u99d7\u99d8\u99d9\u99da\u99db\u99dc\u99dd\u99de\u99df\u99e0\u99e1\u99e2\u99e3\u99e4\u99e5\u99e6\u99e7\u99e8\u99e9\u99ea\u99eb\u99ec\u99ed\u99ee\u99ef\u99f0\u99f1\u99f2\u99f3\u99f4\u99f5\u99f6\u99f7\u99f8\u99f9\u99fa\u99fb\u99fc\u99fd\u99fe\u99ff\u9a00\u9a01\u9a02\u9a03\u9a04\u9a05\u9a06\u9a07\u9a08\u9a09\u9a0a\u9a0b\u9a0c\u9a0d\u9a0e\u9a0f\u9a10\u9a11\u9a12\u9a13\u9a14\u9a15\u9a16\u9a17\u9a18\u9a19\u9a1a\u9a1b\u9a1c\u9a1d\u9a1e\u9a1f\u9a20\u9a21\u9a22\u9a23\u9a24\u9a25\u9a26\u9a27\u9a28\u9a29\u9a2a\u9a2b\u9a2c\u9a2d\u9a2e\u9a2f\u9a30\u9a31\u9a32\u9a33\u9a34\u9a35\u9a36\u9a37\u9a38\u9a39\u9a3a\u9a3b\u9a3c\u9a3d\u9a3e\u9a3f\u9a40\u9a41\u9a42\u9a43\u9a44\u9a45\u9a46\u9a47\u9a48\u9a49\u9a4a\u9a4b\u9a4c\u9a4d\u9a4e\u9a4f\u9a50\u9a51\u9a52\u9a53\u9a54\u9a55\u9a56\u9a57\u9a58\u9a59\u9a5a\u9a5b\u9a5c\u9a5d\u9a5e\u9a5f\u9a60\u9a61\u9a62\u9a63\u9a64\u9a65\u9a66\u9a67\u9a68\u9a69\u9a6a\u9a6b\u9a6c\u9a6d\u9a6e\u9a6f\u9a70\u9a71\u9a72\u9a73\u9a74\u9a75\u9a76\u9a77\u9a78\u9a79\u9a7a\u9a7b\u9a7c\u9a7d\u9a7e\u9a7f\u9a80\u9a81\u9a82\u9a83\u9a84\u9a85\u9a86\u9a87\u9a88\u9a89\u9a8a\u9a8b\u9a8c\u9a8d\u9a8e\u9a8f\u9a90\u9a91\u9a92\u9a93\u9a94\u9a95\u9a96\u9a97\u9a98\u9a99\u9a9a\u9a9b\u9a9c\u9a9d\u9a9e\u9a9f\u9aa0\u9aa1\u9aa2\u9aa3\u9aa4\u9aa5\u9aa6\u9aa7\u9aa8\u9aa9\u9aaa\u9aab\u9aac\u9aad\u9aae\u9aaf\u9ab0\u9ab1\u9ab2\u9ab3\u9ab4\u9ab5\u9ab6\u9ab7\u9ab8\u9ab9\u9aba\u9abb\u9abc\u9abd\u9abe\u9abf\u9ac0\u9ac1\u9ac2\u9ac3\u9ac4\u9ac5\u9ac6\u9ac7\u9ac8\u9ac9\u9aca\u9acb\u9acc\u9acd\u9ace\u9acf\u9ad0\u9ad1\u9ad2\u9ad3\u9ad4\u9ad5\u9ad6\u9ad7\u9ad8\u9ad9\u9ada\u9adb\u9adc\u9add\u9ade\u9adf\u9ae0\u9ae1\u9ae2\u9ae3\u9ae4\u9ae5\u9ae6\u9ae7\u9ae8\u9ae9\u9aea\u9aeb\u9aec\u9aed\u9aee\u9aef\u9af0\u9af1\u9af2\u9af3\u9af4\u9af5\u9af6\u9af7\u9af8\u9af9\u9afa\u9afb\u9afc\u9afd\u9afe\u9aff\u9b00\u9b01\u9b02\u9b03\u9b04\u9b05\u9b06\u9b07\u9b08\u9b09\u9b0a\u9b0b\u9b0c\u9b0d\u9b0e\u9b0f\u9b10\u9b11\u9b12\u9b13\u9b14\u9b15\u9b16\u9b17\u9b18\u9b19\u9b1a\u9b1b\u9b1c\u9b1d\u9b1e\u9b1f\u9b20\u9b21\u9b22\u9b23\u9b24\u9b25\u9b26\u9b27\u9b28\u9b29\u9b2a\u9b2b\u9b2c\u9b2d\u9b2e\u9b2f\u9b30\u9b31\u9b32\u9b33\u9b34\u9b35\u9b36\u9b37\u9b38\u9b39\u9b3a\u9b3b\u9b3c\u9b3d\u9b3e\u9b3f\u9b40\u9b41\u9b42\u9b43\u9b44\u9b45\u9b46\u9b47\u9b48\u9b49\u9b4a\u9b4b\u9b4c\u9b4d\u9b4e\u9b4f\u9b50\u9b51\u9b52\u9b53\u9b54\u9b55\u9b56\u9b57\u9b58\u9b59\u9b5a\u9b5b\u9b5c\u9b5d\u9b5e\u9b5f\u9b60\u9b61\u9b62\u9b63\u9b64\u9b65\u9b66\u9b67\u9b68\u9b69\u9b6a\u9b6b\u9b6c\u9b6d\u9b6e\u9b6f\u9b70\u9b71\u9b72\u9b73\u9b74\u9b75\u9b76\u9b77\u9b78\u9b79\u9b7a\u9b7b\u9b7c\u9b7d\u9b7e\u9b7f\u9b80\u9b81\u9b82\u9b83\u9b84\u9b85\u9b86\u9b87\u9b88\u9b89\u9b8a\u9b8b\u9b8c\u9b8d\u9b8e\u9b8f\u9b90\u9b91\u9b92\u9b93\u9b94\u9b95\u9b96\u9b97\u9b98\u9b99\u9b9a\u9b9b\u9b9c\u9b9d\u9b9e\u9b9f\u9ba0\u9ba1\u9ba2\u9ba3\u9ba4\u9ba5\u9ba6\u9ba7\u9ba8\u9ba9\u9baa\u9bab\u9bac\u9bad\u9bae\u9baf\u9bb0\u9bb1\u9bb2\u9bb3\u9bb4\u9bb5\u9bb6\u9bb7\u9bb8\u9bb9\u9bba\u9bbb\u9bbc\u9bbd\u9bbe\u9bbf\u9bc0\u9bc1\u9bc2\u9bc3\u9bc4\u9bc5\u9bc6\u9bc7\u9bc8\u9bc9\u9bca\u9bcb\u9bcc\u9bcd\u9bce\u9bcf\u9bd0\u9bd1\u9bd2\u9bd3\u9bd4\u9bd5\u9bd6\u9bd7\u9bd8\u9bd9\u9bda\u9bdb\u9bdc\u9bdd\u9bde\u9bdf\u9be0\u9be1\u9be2\u9be3\u9be4\u9be5\u9be6\u9be7\u9be8\u9be9\u9bea\u9beb\u9bec\u9bed\u9bee\u9bef\u9bf0\u9bf1\u9bf2\u9bf3\u9bf4\u9bf5\u9bf6\u9bf7\u9bf8\u9bf9\u9bfa\u9bfb\u9bfc\u9bfd\u9bfe\u9bff\u9c00\u9c01\u9c02\u9c03\u9c04\u9c05\u9c06\u9c07\u9c08\u9c09\u9c0a\u9c0b\u9c0c\u9c0d\u9c0e\u9c0f\u9c10\u9c11\u9c12\u9c13\u9c14\u9c15\u9c16\u9c17\u9c18\u9c19\u9c1a\u9c1b\u9c1c\u9c1d\u9c1e\u9c1f\u9c20\u9c21\u9c22\u9c23\u9c24\u9c25\u9c26\u9c27\u9c28\u9c29\u9c2a\u9c2b\u9c2c\u9c2d\u9c2e\u9c2f\u9c30\u9c31\u9c32\u9c33\u9c34\u9c35\u9c36\u9c37\u9c38\u9c39\u9c3a\u9c3b\u9c3c\u9c3d\u9c3e\u9c3f\u9c40\u9c41\u9c42\u9c43\u9c44\u9c45\u9c46\u9c47\u9c48\u9c49\u9c4a\u9c4b\u9c4c\u9c4d\u9c4e\u9c4f\u9c50\u9c51\u9c52\u9c53\u9c54\u9c55\u9c56\u9c57\u9c58\u9c59\u9c5a\u9c5b\u9c5c\u9c5d\u9c5e\u9c5f\u9c60\u9c61\u9c62\u9c63\u9c64\u9c65\u9c66\u9c67\u9c68\u9c69\u9c6a\u9c6b\u9c6c\u9c6d\u9c6e\u9c6f\u9c70\u9c71\u9c72\u9c73\u9c74\u9c75\u9c76\u9c77\u9c78\u9c79\u9c7a\u9c7b\u9c7c\u9c7d\u9c7e\u9c7f\u9c80\u9c81\u9c82\u9c83\u9c84\u9c85\u9c86\u9c87\u9c88\u9c89\u9c8a\u9c8b\u9c8c\u9c8d\u9c8e\u9c8f\u9c90\u9c91\u9c92\u9c93\u9c94\u9c95\u9c96\u9c97\u9c98\u9c99\u9c9a\u9c9b\u9c9c\u9c9d\u9c9e\u9c9f\u9ca0\u9ca1\u9ca2\u9ca3\u9ca4\u9ca5\u9ca6\u9ca7\u9ca8\u9ca9\u9caa\u9cab\u9cac\u9cad\u9cae\u9caf\u9cb0\u9cb1\u9cb2\u9cb3\u9cb4\u9cb5\u9cb6\u9cb7\u9cb8\u9cb9\u9cba\u9cbb\u9cbc\u9cbd\u9cbe\u9cbf\u9cc0\u9cc1\u9cc2\u9cc3\u9cc4\u9cc5\u9cc6\u9cc7\u9cc8\u9cc9\u9cca\u9ccb\u9ccc\u9ccd\u9cce\u9ccf\u9cd0\u9cd1\u9cd2\u9cd3\u9cd4\u9cd5\u9cd6\u9cd7\u9cd8\u9cd9\u9cda\u9cdb\u9cdc\u9cdd\u9cde\u9cdf\u9ce0\u9ce1\u9ce2\u9ce3\u9ce4\u9ce5\u9ce6\u9ce7\u9ce8\u9ce9\u9cea\u9ceb\u9cec\u9ced\u9cee\u9cef\u9cf0\u9cf1\u9cf2\u9cf3\u9cf4\u9cf5\u9cf6\u9cf7\u9cf8\u9cf9\u9cfa\u9cfb\u9cfc\u9cfd\u9cfe\u9cff\u9d00\u9d01\u9d02\u9d03\u9d04\u9d05\u9d06\u9d07\u9d08\u9d09\u9d0a\u9d0b\u9d0c\u9d0d\u9d0e\u9d0f\u9d10\u9d11\u9d12\u9d13\u9d14\u9d15\u9d16\u9d17\u9d18\u9d19\u9d1a\u9d1b\u9d1c\u9d1d\u9d1e\u9d1f\u9d20\u9d21\u9d22\u9d23\u9d24\u9d25\u9d26\u9d27\u9d28\u9d29\u9d2a\u9d2b\u9d2c\u9d2d\u9d2e\u9d2f\u9d30\u9d31\u9d32\u9d33\u9d34\u9d35\u9d36\u9d37\u9d38\u9d39\u9d3a\u9d3b\u9d3c\u9d3d\u9d3e\u9d3f\u9d40\u9d41\u9d42\u9d43\u9d44\u9d45\u9d46\u9d47\u9d48\u9d49\u9d4a\u9d4b\u9d4c\u9d4d\u9d4e\u9d4f\u9d50\u9d51\u9d52\u9d53\u9d54\u9d55\u9d56\u9d57\u9d58\u9d59\u9d5a\u9d5b\u9d5c\u9d5d\u9d5e\u9d5f\u9d60\u9d61\u9d62\u9d63\u9d64\u9d65\u9d66\u9d67\u9d68\u9d69\u9d6a\u9d6b\u9d6c\u9d6d\u9d6e\u9d6f\u9d70\u9d71\u9d72\u9d73\u9d74\u9d75\u9d76\u9d77\u9d78\u9d79\u9d7a\u9d7b\u9d7c\u9d7d\u9d7e\u9d7f\u9d80\u9d81\u9d82\u9d83\u9d84\u9d85\u9d86\u9d87\u9d88\u9d89\u9d8a\u9d8b\u9d8c\u9d8d\u9d8e\u9d8f\u9d90\u9d91\u9d92\u9d93\u9d94\u9d95\u9d96\u9d97\u9d98\u9d99\u9d9a\u9d9b\u9d9c\u9d9d\u9d9e\u9d9f\u9da0\u9da1\u9da2\u9da3\u9da4\u9da5\u9da6\u9da7\u9da8\u9da9\u9daa\u9dab\u9dac\u9dad\u9dae\u9daf\u9db0\u9db1\u9db2\u9db3\u9db4\u9db5\u9db6\u9db7\u9db8\u9db9\u9dba\u9dbb\u9dbc\u9dbd\u9dbe\u9dbf\u9dc0\u9dc1\u9dc2\u9dc3\u9dc4\u9dc5\u9dc6\u9dc7\u9dc8\u9dc9\u9dca\u9dcb\u9dcc\u9dcd\u9dce\u9dcf\u9dd0\u9dd1\u9dd2\u9dd3\u9dd4\u9dd5\u9dd6\u9dd7\u9dd8\u9dd9\u9dda\u9ddb\u9ddc\u9ddd\u9dde\u9ddf\u9de0\u9de1\u9de2\u9de3\u9de4\u9de5\u9de6\u9de7\u9de8\u9de9\u9dea\u9deb\u9dec\u9ded\u9dee\u9def\u9df0\u9df1\u9df2\u9df3\u9df4\u9df5\u9df6\u9df7\u9df8\u9df9\u9dfa\u9dfb\u9dfc\u9dfd\u9dfe\u9dff\u9e00\u9e01\u9e02\u9e03\u9e04\u9e05\u9e06\u9e07\u9e08\u9e09\u9e0a\u9e0b\u9e0c\u9e0d\u9e0e\u9e0f\u9e10\u9e11\u9e12\u9e13\u9e14\u9e15\u9e16\u9e17\u9e18\u9e19\u9e1a\u9e1b\u9e1c\u9e1d\u9e1e\u9e1f\u9e20\u9e21\u9e22\u9e23\u9e24\u9e25\u9e26\u9e27\u9e28\u9e29\u9e2a\u9e2b\u9e2c\u9e2d\u9e2e\u9e2f\u9e30\u9e31\u9e32\u9e33\u9e34\u9e35\u9e36\u9e37\u9e38\u9e39\u9e3a\u9e3b\u9e3c\u9e3d\u9e3e\u9e3f\u9e40\u9e41\u9e42\u9e43\u9e44\u9e45\u9e46\u9e47\u9e48\u9e49\u9e4a\u9e4b\u9e4c\u9e4d\u9e4e\u9e4f\u9e50\u9e51\u9e52\u9e53\u9e54\u9e55\u9e56\u9e57\u9e58\u9e59\u9e5a\u9e5b\u9e5c\u9e5d\u9e5e\u9e5f\u9e60\u9e61\u9e62\u9e63\u9e64\u9e65\u9e66\u9e67\u9e68\u9e69\u9e6a\u9e6b\u9e6c\u9e6d\u9e6e\u9e6f\u9e70\u9e71\u9e72\u9e73\u9e74\u9e75\u9e76\u9e77\u9e78\u9e79\u9e7a\u9e7b\u9e7c\u9e7d\u9e7e\u9e7f\u9e80\u9e81\u9e82\u9e83\u9e84\u9e85\u9e86\u9e87\u9e88\u9e89\u9e8a\u9e8b\u9e8c\u9e8d\u9e8e\u9e8f\u9e90\u9e91\u9e92\u9e93\u9e94\u9e95\u9e96\u9e97\u9e98\u9e99\u9e9a\u9e9b\u9e9c\u9e9d\u9e9e\u9e9f\u9ea0\u9ea1\u9ea2\u9ea3\u9ea4\u9ea5\u9ea6\u9ea7\u9ea8\u9ea9\u9eaa\u9eab\u9eac\u9ead\u9eae\u9eaf\u9eb0\u9eb1\u9eb2\u9eb3\u9eb4\u9eb5\u9eb6\u9eb7\u9eb8\u9eb9\u9eba\u9ebb\u9ebc\u9ebd\u9ebe\u9ebf\u9ec0\u9ec1\u9ec2\u9ec3\u9ec4\u9ec5\u9ec6\u9ec7\u9ec8\u9ec9\u9eca\u9ecb\u9ecc\u9ecd\u9ece\u9ecf\u9ed0\u9ed1\u9ed2\u9ed3\u9ed4\u9ed5\u9ed6\u9ed7\u9ed8\u9ed9\u9eda\u9edb\u9edc\u9edd\u9ede\u9edf\u9ee0\u9ee1\u9ee2\u9ee3\u9ee4\u9ee5\u9ee6\u9ee7\u9ee8\u9ee9\u9eea\u9eeb\u9eec\u9eed\u9eee\u9eef\u9ef0\u9ef1\u9ef2\u9ef3\u9ef4\u9ef5\u9ef6\u9ef7\u9ef8\u9ef9\u9efa\u9efb\u9efc\u9efd\u9efe\u9eff\u9f00\u9f01\u9f02\u9f03\u9f04\u9f05\u9f06\u9f07\u9f08\u9f09\u9f0a\u9f0b\u9f0c\u9f0d\u9f0e\u9f0f\u9f10\u9f11\u9f12\u9f13\u9f14\u9f15\u9f16\u9f17\u9f18\u9f19\u9f1a\u9f1b\u9f1c\u9f1d\u9f1e\u9f1f\u9f20\u9f21\u9f22\u9f23\u9f24\u9f25\u9f26\u9f27\u9f28\u9f29\u9f2a\u9f2b\u9f2c\u9f2d\u9f2e\u9f2f\u9f30\u9f31\u9f32\u9f33\u9f34\u9f35\u9f36\u9f37\u9f38\u9f39\u9f3a\u9f3b\u9f3c\u9f3d\u9f3e\u9f3f\u9f40\u9f41\u9f42\u9f43\u9f44\u9f45\u9f46\u9f47\u9f48\u9f49\u9f4a\u9f4b\u9f4c\u9f4d\u9f4e\u9f4f\u9f50\u9f51\u9f52\u9f53\u9f54\u9f55\u9f56\u9f57\u9f58\u9f59\u9f5a\u9f5b\u9f5c\u9f5d\u9f5e\u9f5f\u9f60\u9f61\u9f62\u9f63\u9f64\u9f65\u9f66\u9f67\u9f68\u9f69\u9f6a\u9f6b\u9f6c\u9f6d\u9f6e\u9f6f\u9f70\u9f71\u9f72\u9f73\u9f74\u9f75\u9f76\u9f77\u9f78\u9f79\u9f7a\u9f7b\u9f7c\u9f7d\u9f7e\u9f7f\u9f80\u9f81\u9f82\u9f83\u9f84\u9f85\u9f86\u9f87\u9f88\u9f89\u9f8a\u9f8b\u9f8c\u9f8d\u9f8e\u9f8f\u9f90\u9f91\u9f92\u9f93\u9f94\u9f95\u9f96\u9f97\u9f98\u9f99\u9f9a\u9f9b\u9f9c\u9f9d\u9f9e\u9f9f\u9fa0\u9fa1\u9fa2\u9fa3\u9fa4\u9fa5\u9fa6\u9fa7\u9fa8\u9fa9\u9faa\u9fab\u9fac\u9fad\u9fae\u9faf\u9fb0\u9fb1\u9fb2\u9fb3\u9fb4\u9fb5\u9fb6\u9fb7\u9fb8\u9fb9\u9fba\u9fbb\ua000\ua001\ua002\ua003\ua004\ua005\ua006\ua007\ua008\ua009\ua00a\ua00b\ua00c\ua00d\ua00e\ua00f\ua010\ua011\ua012\ua013\ua014\ua016\ua017\ua018\ua019\ua01a\ua01b\ua01c\ua01d\ua01e\ua01f\ua020\ua021\ua022\ua023\ua024\ua025\ua026\ua027\ua028\ua029\ua02a\ua02b\ua02c\ua02d\ua02e\ua02f\ua030\ua031\ua032\ua033\ua034\ua035\ua036\ua037\ua038\ua039\ua03a\ua03b\ua03c\ua03d\ua03e\ua03f\ua040\ua041\ua042\ua043\ua044\ua045\ua046\ua047\ua048\ua049\ua04a\ua04b\ua04c\ua04d\ua04e\ua04f\ua050\ua051\ua052\ua053\ua054\ua055\ua056\ua057\ua058\ua059\ua05a\ua05b\ua05c\ua05d\ua05e\ua05f\ua060\ua061\ua062\ua063\ua064\ua065\ua066\ua067\ua068\ua069\ua06a\ua06b\ua06c\ua06d\ua06e\ua06f\ua070\ua071\ua072\ua073\ua074\ua075\ua076\ua077\ua078\ua079\ua07a\ua07b\ua07c\ua07d\ua07e\ua07f\ua080\ua081\ua082\ua083\ua084\ua085\ua086\ua087\ua088\ua089\ua08a\ua08b\ua08c\ua08d\ua08e\ua08f\ua090\ua091\ua092\ua093\ua094\ua095\ua096\ua097\ua098\ua099\ua09a\ua09b\ua09c\ua09d\ua09e\ua09f\ua0a0\ua0a1\ua0a2\ua0a3\ua0a4\ua0a5\ua0a6\ua0a7\ua0a8\ua0a9\ua0aa\ua0ab\ua0ac\ua0ad\ua0ae\ua0af\ua0b0\ua0b1\ua0b2\ua0b3\ua0b4\ua0b5\ua0b6\ua0b7\ua0b8\ua0b9\ua0ba\ua0bb\ua0bc\ua0bd\ua0be\ua0bf\ua0c0\ua0c1\ua0c2\ua0c3\ua0c4\ua0c5\ua0c6\ua0c7\ua0c8\ua0c9\ua0ca\ua0cb\ua0cc\ua0cd\ua0ce\ua0cf\ua0d0\ua0d1\ua0d2\ua0d3\ua0d4\ua0d5\ua0d6\ua0d7\ua0d8\ua0d9\ua0da\ua0db\ua0dc\ua0dd\ua0de\ua0df\ua0e0\ua0e1\ua0e2\ua0e3\ua0e4\ua0e5\ua0e6\ua0e7\ua0e8\ua0e9\ua0ea\ua0eb\ua0ec\ua0ed\ua0ee\ua0ef\ua0f0\ua0f1\ua0f2\ua0f3\ua0f4\ua0f5\ua0f6\ua0f7\ua0f8\ua0f9\ua0fa\ua0fb\ua0fc\ua0fd\ua0fe\ua0ff\ua100\ua101\ua102\ua103\ua104\ua105\ua106\ua107\ua108\ua109\ua10a\ua10b\ua10c\ua10d\ua10e\ua10f\ua110\ua111\ua112\ua113\ua114\ua115\ua116\ua117\ua118\ua119\ua11a\ua11b\ua11c\ua11d\ua11e\ua11f\ua120\ua121\ua122\ua123\ua124\ua125\ua126\ua127\ua128\ua129\ua12a\ua12b\ua12c\ua12d\ua12e\ua12f\ua130\ua131\ua132\ua133\ua134\ua135\ua136\ua137\ua138\ua139\ua13a\ua13b\ua13c\ua13d\ua13e\ua13f\ua140\ua141\ua142\ua143\ua144\ua145\ua146\ua147\ua148\ua149\ua14a\ua14b\ua14c\ua14d\ua14e\ua14f\ua150\ua151\ua152\ua153\ua154\ua155\ua156\ua157\ua158\ua159\ua15a\ua15b\ua15c\ua15d\ua15e\ua15f\ua160\ua161\ua162\ua163\ua164\ua165\ua166\ua167\ua168\ua169\ua16a\ua16b\ua16c\ua16d\ua16e\ua16f\ua170\ua171\ua172\ua173\ua174\ua175\ua176\ua177\ua178\ua179\ua17a\ua17b\ua17c\ua17d\ua17e\ua17f\ua180\ua181\ua182\ua183\ua184\ua185\ua186\ua187\ua188\ua189\ua18a\ua18b\ua18c\ua18d\ua18e\ua18f\ua190\ua191\ua192\ua193\ua194\ua195\ua196\ua197\ua198\ua199\ua19a\ua19b\ua19c\ua19d\ua19e\ua19f\ua1a0\ua1a1\ua1a2\ua1a3\ua1a4\ua1a5\ua1a6\ua1a7\ua1a8\ua1a9\ua1aa\ua1ab\ua1ac\ua1ad\ua1ae\ua1af\ua1b0\ua1b1\ua1b2\ua1b3\ua1b4\ua1b5\ua1b6\ua1b7\ua1b8\ua1b9\ua1ba\ua1bb\ua1bc\ua1bd\ua1be\ua1bf\ua1c0\ua1c1\ua1c2\ua1c3\ua1c4\ua1c5\ua1c6\ua1c7\ua1c8\ua1c9\ua1ca\ua1cb\ua1cc\ua1cd\ua1ce\ua1cf\ua1d0\ua1d1\ua1d2\ua1d3\ua1d4\ua1d5\ua1d6\ua1d7\ua1d8\ua1d9\ua1da\ua1db\ua1dc\ua1dd\ua1de\ua1df\ua1e0\ua1e1\ua1e2\ua1e3\ua1e4\ua1e5\ua1e6\ua1e7\ua1e8\ua1e9\ua1ea\ua1eb\ua1ec\ua1ed\ua1ee\ua1ef\ua1f0\ua1f1\ua1f2\ua1f3\ua1f4\ua1f5\ua1f6\ua1f7\ua1f8\ua1f9\ua1fa\ua1fb\ua1fc\ua1fd\ua1fe\ua1ff\ua200\ua201\ua202\ua203\ua204\ua205\ua206\ua207\ua208\ua209\ua20a\ua20b\ua20c\ua20d\ua20e\ua20f\ua210\ua211\ua212\ua213\ua214\ua215\ua216\ua217\ua218\ua219\ua21a\ua21b\ua21c\ua21d\ua21e\ua21f\ua220\ua221\ua222\ua223\ua224\ua225\ua226\ua227\ua228\ua229\ua22a\ua22b\ua22c\ua22d\ua22e\ua22f\ua230\ua231\ua232\ua233\ua234\ua235\ua236\ua237\ua238\ua239\ua23a\ua23b\ua23c\ua23d\ua23e\ua23f\ua240\ua241\ua242\ua243\ua244\ua245\ua246\ua247\ua248\ua249\ua24a\ua24b\ua24c\ua24d\ua24e\ua24f\ua250\ua251\ua252\ua253\ua254\ua255\ua256\ua257\ua258\ua259\ua25a\ua25b\ua25c\ua25d\ua25e\ua25f\ua260\ua261\ua262\ua263\ua264\ua265\ua266\ua267\ua268\ua269\ua26a\ua26b\ua26c\ua26d\ua26e\ua26f\ua270\ua271\ua272\ua273\ua274\ua275\ua276\ua277\ua278\ua279\ua27a\ua27b\ua27c\ua27d\ua27e\ua27f\ua280\ua281\ua282\ua283\ua284\ua285\ua286\ua287\ua288\ua289\ua28a\ua28b\ua28c\ua28d\ua28e\ua28f\ua290\ua291\ua292\ua293\ua294\ua295\ua296\ua297\ua298\ua299\ua29a\ua29b\ua29c\ua29d\ua29e\ua29f\ua2a0\ua2a1\ua2a2\ua2a3\ua2a4\ua2a5\ua2a6\ua2a7\ua2a8\ua2a9\ua2aa\ua2ab\ua2ac\ua2ad\ua2ae\ua2af\ua2b0\ua2b1\ua2b2\ua2b3\ua2b4\ua2b5\ua2b6\ua2b7\ua2b8\ua2b9\ua2ba\ua2bb\ua2bc\ua2bd\ua2be\ua2bf\ua2c0\ua2c1\ua2c2\ua2c3\ua2c4\ua2c5\ua2c6\ua2c7\ua2c8\ua2c9\ua2ca\ua2cb\ua2cc\ua2cd\ua2ce\ua2cf\ua2d0\ua2d1\ua2d2\ua2d3\ua2d4\ua2d5\ua2d6\ua2d7\ua2d8\ua2d9\ua2da\ua2db\ua2dc\ua2dd\ua2de\ua2df\ua2e0\ua2e1\ua2e2\ua2e3\ua2e4\ua2e5\ua2e6\ua2e7\ua2e8\ua2e9\ua2ea\ua2eb\ua2ec\ua2ed\ua2ee\ua2ef\ua2f0\ua2f1\ua2f2\ua2f3\ua2f4\ua2f5\ua2f6\ua2f7\ua2f8\ua2f9\ua2fa\ua2fb\ua2fc\ua2fd\ua2fe\ua2ff\ua300\ua301\ua302\ua303\ua304\ua305\ua306\ua307\ua308\ua309\ua30a\ua30b\ua30c\ua30d\ua30e\ua30f\ua310\ua311\ua312\ua313\ua314\ua315\ua316\ua317\ua318\ua319\ua31a\ua31b\ua31c\ua31d\ua31e\ua31f\ua320\ua321\ua322\ua323\ua324\ua325\ua326\ua327\ua328\ua329\ua32a\ua32b\ua32c\ua32d\ua32e\ua32f\ua330\ua331\ua332\ua333\ua334\ua335\ua336\ua337\ua338\ua339\ua33a\ua33b\ua33c\ua33d\ua33e\ua33f\ua340\ua341\ua342\ua343\ua344\ua345\ua346\ua347\ua348\ua349\ua34a\ua34b\ua34c\ua34d\ua34e\ua34f\ua350\ua351\ua352\ua353\ua354\ua355\ua356\ua357\ua358\ua359\ua35a\ua35b\ua35c\ua35d\ua35e\ua35f\ua360\ua361\ua362\ua363\ua364\ua365\ua366\ua367\ua368\ua369\ua36a\ua36b\ua36c\ua36d\ua36e\ua36f\ua370\ua371\ua372\ua373\ua374\ua375\ua376\ua377\ua378\ua379\ua37a\ua37b\ua37c\ua37d\ua37e\ua37f\ua380\ua381\ua382\ua383\ua384\ua385\ua386\ua387\ua388\ua389\ua38a\ua38b\ua38c\ua38d\ua38e\ua38f\ua390\ua391\ua392\ua393\ua394\ua395\ua396\ua397\ua398\ua399\ua39a\ua39b\ua39c\ua39d\ua39e\ua39f\ua3a0\ua3a1\ua3a2\ua3a3\ua3a4\ua3a5\ua3a6\ua3a7\ua3a8\ua3a9\ua3aa\ua3ab\ua3ac\ua3ad\ua3ae\ua3af\ua3b0\ua3b1\ua3b2\ua3b3\ua3b4\ua3b5\ua3b6\ua3b7\ua3b8\ua3b9\ua3ba\ua3bb\ua3bc\ua3bd\ua3be\ua3bf\ua3c0\ua3c1\ua3c2\ua3c3\ua3c4\ua3c5\ua3c6\ua3c7\ua3c8\ua3c9\ua3ca\ua3cb\ua3cc\ua3cd\ua3ce\ua3cf\ua3d0\ua3d1\ua3d2\ua3d3\ua3d4\ua3d5\ua3d6\ua3d7\ua3d8\ua3d9\ua3da\ua3db\ua3dc\ua3dd\ua3de\ua3df\ua3e0\ua3e1\ua3e2\ua3e3\ua3e4\ua3e5\ua3e6\ua3e7\ua3e8\ua3e9\ua3ea\ua3eb\ua3ec\ua3ed\ua3ee\ua3ef\ua3f0\ua3f1\ua3f2\ua3f3\ua3f4\ua3f5\ua3f6\ua3f7\ua3f8\ua3f9\ua3fa\ua3fb\ua3fc\ua3fd\ua3fe\ua3ff\ua400\ua401\ua402\ua403\ua404\ua405\ua406\ua407\ua408\ua409\ua40a\ua40b\ua40c\ua40d\ua40e\ua40f\ua410\ua411\ua412\ua413\ua414\ua415\ua416\ua417\ua418\ua419\ua41a\ua41b\ua41c\ua41d\ua41e\ua41f\ua420\ua421\ua422\ua423\ua424\ua425\ua426\ua427\ua428\ua429\ua42a\ua42b\ua42c\ua42d\ua42e\ua42f\ua430\ua431\ua432\ua433\ua434\ua435\ua436\ua437\ua438\ua439\ua43a\ua43b\ua43c\ua43d\ua43e\ua43f\ua440\ua441\ua442\ua443\ua444\ua445\ua446\ua447\ua448\ua449\ua44a\ua44b\ua44c\ua44d\ua44e\ua44f\ua450\ua451\ua452\ua453\ua454\ua455\ua456\ua457\ua458\ua459\ua45a\ua45b\ua45c\ua45d\ua45e\ua45f\ua460\ua461\ua462\ua463\ua464\ua465\ua466\ua467\ua468\ua469\ua46a\ua46b\ua46c\ua46d\ua46e\ua46f\ua470\ua471\ua472\ua473\ua474\ua475\ua476\ua477\ua478\ua479\ua47a\ua47b\ua47c\ua47d\ua47e\ua47f\ua480\ua481\ua482\ua483\ua484\ua485\ua486\ua487\ua488\ua489\ua48a\ua48b\ua48c\ua800\ua801\ua803\ua804\ua805\ua807\ua808\ua809\ua80a\ua80c\ua80d\ua80e\ua80f\ua810\ua811\ua812\ua813\ua814\ua815\ua816\ua817\ua818\ua819\ua81a\ua81b\ua81c\ua81d\ua81e\ua81f\ua820\ua821\ua822\uac00\uac01\uac02\uac03\uac04\uac05\uac06\uac07\uac08\uac09\uac0a\uac0b\uac0c\uac0d\uac0e\uac0f\uac10\uac11\uac12\uac13\uac14\uac15\uac16\uac17\uac18\uac19\uac1a\uac1b\uac1c\uac1d\uac1e\uac1f\uac20\uac21\uac22\uac23\uac24\uac25\uac26\uac27\uac28\uac29\uac2a\uac2b\uac2c\uac2d\uac2e\uac2f\uac30\uac31\uac32\uac33\uac34\uac35\uac36\uac37\uac38\uac39\uac3a\uac3b\uac3c\uac3d\uac3e\uac3f\uac40\uac41\uac42\uac43\uac44\uac45\uac46\uac47\uac48\uac49\uac4a\uac4b\uac4c\uac4d\uac4e\uac4f\uac50\uac51\uac52\uac53\uac54\uac55\uac56\uac57\uac58\uac59\uac5a\uac5b\uac5c\uac5d\uac5e\uac5f\uac60\uac61\uac62\uac63\uac64\uac65\uac66\uac67\uac68\uac69\uac6a\uac6b\uac6c\uac6d\uac6e\uac6f\uac70\uac71\uac72\uac73\uac74\uac75\uac76\uac77\uac78\uac79\uac7a\uac7b\uac7c\uac7d\uac7e\uac7f\uac80\uac81\uac82\uac83\uac84\uac85\uac86\uac87\uac88\uac89\uac8a\uac8b\uac8c\uac8d\uac8e\uac8f\uac90\uac91\uac92\uac93\uac94\uac95\uac96\uac97\uac98\uac99\uac9a\uac9b\uac9c\uac9d\uac9e\uac9f\uaca0\uaca1\uaca2\uaca3\uaca4\uaca5\uaca6\uaca7\uaca8\uaca9\uacaa\uacab\uacac\uacad\uacae\uacaf\uacb0\uacb1\uacb2\uacb3\uacb4\uacb5\uacb6\uacb7\uacb8\uacb9\uacba\uacbb\uacbc\uacbd\uacbe\uacbf\uacc0\uacc1\uacc2\uacc3\uacc4\uacc5\uacc6\uacc7\uacc8\uacc9\uacca\uaccb\uaccc\uaccd\uacce\uaccf\uacd0\uacd1\uacd2\uacd3\uacd4\uacd5\uacd6\uacd7\uacd8\uacd9\uacda\uacdb\uacdc\uacdd\uacde\uacdf\uace0\uace1\uace2\uace3\uace4\uace5\uace6\uace7\uace8\uace9\uacea\uaceb\uacec\uaced\uacee\uacef\uacf0\uacf1\uacf2\uacf3\uacf4\uacf5\uacf6\uacf7\uacf8\uacf9\uacfa\uacfb\uacfc\uacfd\uacfe\uacff\uad00\uad01\uad02\uad03\uad04\uad05\uad06\uad07\uad08\uad09\uad0a\uad0b\uad0c\uad0d\uad0e\uad0f\uad10\uad11\uad12\uad13\uad14\uad15\uad16\uad17\uad18\uad19\uad1a\uad1b\uad1c\uad1d\uad1e\uad1f\uad20\uad21\uad22\uad23\uad24\uad25\uad26\uad27\uad28\uad29\uad2a\uad2b\uad2c\uad2d\uad2e\uad2f\uad30\uad31\uad32\uad33\uad34\uad35\uad36\uad37\uad38\uad39\uad3a\uad3b\uad3c\uad3d\uad3e\uad3f\uad40\uad41\uad42\uad43\uad44\uad45\uad46\uad47\uad48\uad49\uad4a\uad4b\uad4c\uad4d\uad4e\uad4f\uad50\uad51\uad52\uad53\uad54\uad55\uad56\uad57\uad58\uad59\uad5a\uad5b\uad5c\uad5d\uad5e\uad5f\uad60\uad61\uad62\uad63\uad64\uad65\uad66\uad67\uad68\uad69\uad6a\uad6b\uad6c\uad6d\uad6e\uad6f\uad70\uad71\uad72\uad73\uad74\uad75\uad76\uad77\uad78\uad79\uad7a\uad7b\uad7c\uad7d\uad7e\uad7f\uad80\uad81\uad82\uad83\uad84\uad85\uad86\uad87\uad88\uad89\uad8a\uad8b\uad8c\uad8d\uad8e\uad8f\uad90\uad91\uad92\uad93\uad94\uad95\uad96\uad97\uad98\uad99\uad9a\uad9b\uad9c\uad9d\uad9e\uad9f\uada0\uada1\uada2\uada3\uada4\uada5\uada6\uada7\uada8\uada9\uadaa\uadab\uadac\uadad\uadae\uadaf\uadb0\uadb1\uadb2\uadb3\uadb4\uadb5\uadb6\uadb7\uadb8\uadb9\uadba\uadbb\uadbc\uadbd\uadbe\uadbf\uadc0\uadc1\uadc2\uadc3\uadc4\uadc5\uadc6\uadc7\uadc8\uadc9\uadca\uadcb\uadcc\uadcd\uadce\uadcf\uadd0\uadd1\uadd2\uadd3\uadd4\uadd5\uadd6\uadd7\uadd8\uadd9\uadda\uaddb\uaddc\uaddd\uadde\uaddf\uade0\uade1\uade2\uade3\uade4\uade5\uade6\uade7\uade8\uade9\uadea\uadeb\uadec\uaded\uadee\uadef\uadf0\uadf1\uadf2\uadf3\uadf4\uadf5\uadf6\uadf7\uadf8\uadf9\uadfa\uadfb\uadfc\uadfd\uadfe\uadff\uae00\uae01\uae02\uae03\uae04\uae05\uae06\uae07\uae08\uae09\uae0a\uae0b\uae0c\uae0d\uae0e\uae0f\uae10\uae11\uae12\uae13\uae14\uae15\uae16\uae17\uae18\uae19\uae1a\uae1b\uae1c\uae1d\uae1e\uae1f\uae20\uae21\uae22\uae23\uae24\uae25\uae26\uae27\uae28\uae29\uae2a\uae2b\uae2c\uae2d\uae2e\uae2f\uae30\uae31\uae32\uae33\uae34\uae35\uae36\uae37\uae38\uae39\uae3a\uae3b\uae3c\uae3d\uae3e\uae3f\uae40\uae41\uae42\uae43\uae44\uae45\uae46\uae47\uae48\uae49\uae4a\uae4b\uae4c\uae4d\uae4e\uae4f\uae50\uae51\uae52\uae53\uae54\uae55\uae56\uae57\uae58\uae59\uae5a\uae5b\uae5c\uae5d\uae5e\uae5f\uae60\uae61\uae62\uae63\uae64\uae65\uae66\uae67\uae68\uae69\uae6a\uae6b\uae6c\uae6d\uae6e\uae6f\uae70\uae71\uae72\uae73\uae74\uae75\uae76\uae77\uae78\uae79\uae7a\uae7b\uae7c\uae7d\uae7e\uae7f\uae80\uae81\uae82\uae83\uae84\uae85\uae86\uae87\uae88\uae89\uae8a\uae8b\uae8c\uae8d\uae8e\uae8f\uae90\uae91\uae92\uae93\uae94\uae95\uae96\uae97\uae98\uae99\uae9a\uae9b\uae9c\uae9d\uae9e\uae9f\uaea0\uaea1\uaea2\uaea3\uaea4\uaea5\uaea6\uaea7\uaea8\uaea9\uaeaa\uaeab\uaeac\uaead\uaeae\uaeaf\uaeb0\uaeb1\uaeb2\uaeb3\uaeb4\uaeb5\uaeb6\uaeb7\uaeb8\uaeb9\uaeba\uaebb\uaebc\uaebd\uaebe\uaebf\uaec0\uaec1\uaec2\uaec3\uaec4\uaec5\uaec6\uaec7\uaec8\uaec9\uaeca\uaecb\uaecc\uaecd\uaece\uaecf\uaed0\uaed1\uaed2\uaed3\uaed4\uaed5\uaed6\uaed7\uaed8\uaed9\uaeda\uaedb\uaedc\uaedd\uaede\uaedf\uaee0\uaee1\uaee2\uaee3\uaee4\uaee5\uaee6\uaee7\uaee8\uaee9\uaeea\uaeeb\uaeec\uaeed\uaeee\uaeef\uaef0\uaef1\uaef2\uaef3\uaef4\uaef5\uaef6\uaef7\uaef8\uaef9\uaefa\uaefb\uaefc\uaefd\uaefe\uaeff\uaf00\uaf01\uaf02\uaf03\uaf04\uaf05\uaf06\uaf07\uaf08\uaf09\uaf0a\uaf0b\uaf0c\uaf0d\uaf0e\uaf0f\uaf10\uaf11\uaf12\uaf13\uaf14\uaf15\uaf16\uaf17\uaf18\uaf19\uaf1a\uaf1b\uaf1c\uaf1d\uaf1e\uaf1f\uaf20\uaf21\uaf22\uaf23\uaf24\uaf25\uaf26\uaf27\uaf28\uaf29\uaf2a\uaf2b\uaf2c\uaf2d\uaf2e\uaf2f\uaf30\uaf31\uaf32\uaf33\uaf34\uaf35\uaf36\uaf37\uaf38\uaf39\uaf3a\uaf3b\uaf3c\uaf3d\uaf3e\uaf3f\uaf40\uaf41\uaf42\uaf43\uaf44\uaf45\uaf46\uaf47\uaf48\uaf49\uaf4a\uaf4b\uaf4c\uaf4d\uaf4e\uaf4f\uaf50\uaf51\uaf52\uaf53\uaf54\uaf55\uaf56\uaf57\uaf58\uaf59\uaf5a\uaf5b\uaf5c\uaf5d\uaf5e\uaf5f\uaf60\uaf61\uaf62\uaf63\uaf64\uaf65\uaf66\uaf67\uaf68\uaf69\uaf6a\uaf6b\uaf6c\uaf6d\uaf6e\uaf6f\uaf70\uaf71\uaf72\uaf73\uaf74\uaf75\uaf76\uaf77\uaf78\uaf79\uaf7a\uaf7b\uaf7c\uaf7d\uaf7e\uaf7f\uaf80\uaf81\uaf82\uaf83\uaf84\uaf85\uaf86\uaf87\uaf88\uaf89\uaf8a\uaf8b\uaf8c\uaf8d\uaf8e\uaf8f\uaf90\uaf91\uaf92\uaf93\uaf94\uaf95\uaf96\uaf97\uaf98\uaf99\uaf9a\uaf9b\uaf9c\uaf9d\uaf9e\uaf9f\uafa0\uafa1\uafa2\uafa3\uafa4\uafa5\uafa6\uafa7\uafa8\uafa9\uafaa\uafab\uafac\uafad\uafae\uafaf\uafb0\uafb1\uafb2\uafb3\uafb4\uafb5\uafb6\uafb7\uafb8\uafb9\uafba\uafbb\uafbc\uafbd\uafbe\uafbf\uafc0\uafc1\uafc2\uafc3\uafc4\uafc5\uafc6\uafc7\uafc8\uafc9\uafca\uafcb\uafcc\uafcd\uafce\uafcf\uafd0\uafd1\uafd2\uafd3\uafd4\uafd5\uafd6\uafd7\uafd8\uafd9\uafda\uafdb\uafdc\uafdd\uafde\uafdf\uafe0\uafe1\uafe2\uafe3\uafe4\uafe5\uafe6\uafe7\uafe8\uafe9\uafea\uafeb\uafec\uafed\uafee\uafef\uaff0\uaff1\uaff2\uaff3\uaff4\uaff5\uaff6\uaff7\uaff8\uaff9\uaffa\uaffb\uaffc\uaffd\uaffe\uafff\ub000\ub001\ub002\ub003\ub004\ub005\ub006\ub007\ub008\ub009\ub00a\ub00b\ub00c\ub00d\ub00e\ub00f\ub010\ub011\ub012\ub013\ub014\ub015\ub016\ub017\ub018\ub019\ub01a\ub01b\ub01c\ub01d\ub01e\ub01f\ub020\ub021\ub022\ub023\ub024\ub025\ub026\ub027\ub028\ub029\ub02a\ub02b\ub02c\ub02d\ub02e\ub02f\ub030\ub031\ub032\ub033\ub034\ub035\ub036\ub037\ub038\ub039\ub03a\ub03b\ub03c\ub03d\ub03e\ub03f\ub040\ub041\ub042\ub043\ub044\ub045\ub046\ub047\ub048\ub049\ub04a\ub04b\ub04c\ub04d\ub04e\ub04f\ub050\ub051\ub052\ub053\ub054\ub055\ub056\ub057\ub058\ub059\ub05a\ub05b\ub05c\ub05d\ub05e\ub05f\ub060\ub061\ub062\ub063\ub064\ub065\ub066\ub067\ub068\ub069\ub06a\ub06b\ub06c\ub06d\ub06e\ub06f\ub070\ub071\ub072\ub073\ub074\ub075\ub076\ub077\ub078\ub079\ub07a\ub07b\ub07c\ub07d\ub07e\ub07f\ub080\ub081\ub082\ub083\ub084\ub085\ub086\ub087\ub088\ub089\ub08a\ub08b\ub08c\ub08d\ub08e\ub08f\ub090\ub091\ub092\ub093\ub094\ub095\ub096\ub097\ub098\ub099\ub09a\ub09b\ub09c\ub09d\ub09e\ub09f\ub0a0\ub0a1\ub0a2\ub0a3\ub0a4\ub0a5\ub0a6\ub0a7\ub0a8\ub0a9\ub0aa\ub0ab\ub0ac\ub0ad\ub0ae\ub0af\ub0b0\ub0b1\ub0b2\ub0b3\ub0b4\ub0b5\ub0b6\ub0b7\ub0b8\ub0b9\ub0ba\ub0bb\ub0bc\ub0bd\ub0be\ub0bf\ub0c0\ub0c1\ub0c2\ub0c3\ub0c4\ub0c5\ub0c6\ub0c7\ub0c8\ub0c9\ub0ca\ub0cb\ub0cc\ub0cd\ub0ce\ub0cf\ub0d0\ub0d1\ub0d2\ub0d3\ub0d4\ub0d5\ub0d6\ub0d7\ub0d8\ub0d9\ub0da\ub0db\ub0dc\ub0dd\ub0de\ub0df\ub0e0\ub0e1\ub0e2\ub0e3\ub0e4\ub0e5\ub0e6\ub0e7\ub0e8\ub0e9\ub0ea\ub0eb\ub0ec\ub0ed\ub0ee\ub0ef\ub0f0\ub0f1\ub0f2\ub0f3\ub0f4\ub0f5\ub0f6\ub0f7\ub0f8\ub0f9\ub0fa\ub0fb\ub0fc\ub0fd\ub0fe\ub0ff\ub100\ub101\ub102\ub103\ub104\ub105\ub106\ub107\ub108\ub109\ub10a\ub10b\ub10c\ub10d\ub10e\ub10f\ub110\ub111\ub112\ub113\ub114\ub115\ub116\ub117\ub118\ub119\ub11a\ub11b\ub11c\ub11d\ub11e\ub11f\ub120\ub121\ub122\ub123\ub124\ub125\ub126\ub127\ub128\ub129\ub12a\ub12b\ub12c\ub12d\ub12e\ub12f\ub130\ub131\ub132\ub133\ub134\ub135\ub136\ub137\ub138\ub139\ub13a\ub13b\ub13c\ub13d\ub13e\ub13f\ub140\ub141\ub142\ub143\ub144\ub145\ub146\ub147\ub148\ub149\ub14a\ub14b\ub14c\ub14d\ub14e\ub14f\ub150\ub151\ub152\ub153\ub154\ub155\ub156\ub157\ub158\ub159\ub15a\ub15b\ub15c\ub15d\ub15e\ub15f\ub160\ub161\ub162\ub163\ub164\ub165\ub166\ub167\ub168\ub169\ub16a\ub16b\ub16c\ub16d\ub16e\ub16f\ub170\ub171\ub172\ub173\ub174\ub175\ub176\ub177\ub178\ub179\ub17a\ub17b\ub17c\ub17d\ub17e\ub17f\ub180\ub181\ub182\ub183\ub184\ub185\ub186\ub187\ub188\ub189\ub18a\ub18b\ub18c\ub18d\ub18e\ub18f\ub190\ub191\ub192\ub193\ub194\ub195\ub196\ub197\ub198\ub199\ub19a\ub19b\ub19c\ub19d\ub19e\ub19f\ub1a0\ub1a1\ub1a2\ub1a3\ub1a4\ub1a5\ub1a6\ub1a7\ub1a8\ub1a9\ub1aa\ub1ab\ub1ac\ub1ad\ub1ae\ub1af\ub1b0\ub1b1\ub1b2\ub1b3\ub1b4\ub1b5\ub1b6\ub1b7\ub1b8\ub1b9\ub1ba\ub1bb\ub1bc\ub1bd\ub1be\ub1bf\ub1c0\ub1c1\ub1c2\ub1c3\ub1c4\ub1c5\ub1c6\ub1c7\ub1c8\ub1c9\ub1ca\ub1cb\ub1cc\ub1cd\ub1ce\ub1cf\ub1d0\ub1d1\ub1d2\ub1d3\ub1d4\ub1d5\ub1d6\ub1d7\ub1d8\ub1d9\ub1da\ub1db\ub1dc\ub1dd\ub1de\ub1df\ub1e0\ub1e1\ub1e2\ub1e3\ub1e4\ub1e5\ub1e6\ub1e7\ub1e8\ub1e9\ub1ea\ub1eb\ub1ec\ub1ed\ub1ee\ub1ef\ub1f0\ub1f1\ub1f2\ub1f3\ub1f4\ub1f5\ub1f6\ub1f7\ub1f8\ub1f9\ub1fa\ub1fb\ub1fc\ub1fd\ub1fe\ub1ff\ub200\ub201\ub202\ub203\ub204\ub205\ub206\ub207\ub208\ub209\ub20a\ub20b\ub20c\ub20d\ub20e\ub20f\ub210\ub211\ub212\ub213\ub214\ub215\ub216\ub217\ub218\ub219\ub21a\ub21b\ub21c\ub21d\ub21e\ub21f\ub220\ub221\ub222\ub223\ub224\ub225\ub226\ub227\ub228\ub229\ub22a\ub22b\ub22c\ub22d\ub22e\ub22f\ub230\ub231\ub232\ub233\ub234\ub235\ub236\ub237\ub238\ub239\ub23a\ub23b\ub23c\ub23d\ub23e\ub23f\ub240\ub241\ub242\ub243\ub244\ub245\ub246\ub247\ub248\ub249\ub24a\ub24b\ub24c\ub24d\ub24e\ub24f\ub250\ub251\ub252\ub253\ub254\ub255\ub256\ub257\ub258\ub259\ub25a\ub25b\ub25c\ub25d\ub25e\ub25f\ub260\ub261\ub262\ub263\ub264\ub265\ub266\ub267\ub268\ub269\ub26a\ub26b\ub26c\ub26d\ub26e\ub26f\ub270\ub271\ub272\ub273\ub274\ub275\ub276\ub277\ub278\ub279\ub27a\ub27b\ub27c\ub27d\ub27e\ub27f\ub280\ub281\ub282\ub283\ub284\ub285\ub286\ub287\ub288\ub289\ub28a\ub28b\ub28c\ub28d\ub28e\ub28f\ub290\ub291\ub292\ub293\ub294\ub295\ub296\ub297\ub298\ub299\ub29a\ub29b\ub29c\ub29d\ub29e\ub29f\ub2a0\ub2a1\ub2a2\ub2a3\ub2a4\ub2a5\ub2a6\ub2a7\ub2a8\ub2a9\ub2aa\ub2ab\ub2ac\ub2ad\ub2ae\ub2af\ub2b0\ub2b1\ub2b2\ub2b3\ub2b4\ub2b5\ub2b6\ub2b7\ub2b8\ub2b9\ub2ba\ub2bb\ub2bc\ub2bd\ub2be\ub2bf\ub2c0\ub2c1\ub2c2\ub2c3\ub2c4\ub2c5\ub2c6\ub2c7\ub2c8\ub2c9\ub2ca\ub2cb\ub2cc\ub2cd\ub2ce\ub2cf\ub2d0\ub2d1\ub2d2\ub2d3\ub2d4\ub2d5\ub2d6\ub2d7\ub2d8\ub2d9\ub2da\ub2db\ub2dc\ub2dd\ub2de\ub2df\ub2e0\ub2e1\ub2e2\ub2e3\ub2e4\ub2e5\ub2e6\ub2e7\ub2e8\ub2e9\ub2ea\ub2eb\ub2ec\ub2ed\ub2ee\ub2ef\ub2f0\ub2f1\ub2f2\ub2f3\ub2f4\ub2f5\ub2f6\ub2f7\ub2f8\ub2f9\ub2fa\ub2fb\ub2fc\ub2fd\ub2fe\ub2ff\ub300\ub301\ub302\ub303\ub304\ub305\ub306\ub307\ub308\ub309\ub30a\ub30b\ub30c\ub30d\ub30e\ub30f\ub310\ub311\ub312\ub313\ub314\ub315\ub316\ub317\ub318\ub319\ub31a\ub31b\ub31c\ub31d\ub31e\ub31f\ub320\ub321\ub322\ub323\ub324\ub325\ub326\ub327\ub328\ub329\ub32a\ub32b\ub32c\ub32d\ub32e\ub32f\ub330\ub331\ub332\ub333\ub334\ub335\ub336\ub337\ub338\ub339\ub33a\ub33b\ub33c\ub33d\ub33e\ub33f\ub340\ub341\ub342\ub343\ub344\ub345\ub346\ub347\ub348\ub349\ub34a\ub34b\ub34c\ub34d\ub34e\ub34f\ub350\ub351\ub352\ub353\ub354\ub355\ub356\ub357\ub358\ub359\ub35a\ub35b\ub35c\ub35d\ub35e\ub35f\ub360\ub361\ub362\ub363\ub364\ub365\ub366\ub367\ub368\ub369\ub36a\ub36b\ub36c\ub36d\ub36e\ub36f\ub370\ub371\ub372\ub373\ub374\ub375\ub376\ub377\ub378\ub379\ub37a\ub37b\ub37c\ub37d\ub37e\ub37f\ub380\ub381\ub382\ub383\ub384\ub385\ub386\ub387\ub388\ub389\ub38a\ub38b\ub38c\ub38d\ub38e\ub38f\ub390\ub391\ub392\ub393\ub394\ub395\ub396\ub397\ub398\ub399\ub39a\ub39b\ub39c\ub39d\ub39e\ub39f\ub3a0\ub3a1\ub3a2\ub3a3\ub3a4\ub3a5\ub3a6\ub3a7\ub3a8\ub3a9\ub3aa\ub3ab\ub3ac\ub3ad\ub3ae\ub3af\ub3b0\ub3b1\ub3b2\ub3b3\ub3b4\ub3b5\ub3b6\ub3b7\ub3b8\ub3b9\ub3ba\ub3bb\ub3bc\ub3bd\ub3be\ub3bf\ub3c0\ub3c1\ub3c2\ub3c3\ub3c4\ub3c5\ub3c6\ub3c7\ub3c8\ub3c9\ub3ca\ub3cb\ub3cc\ub3cd\ub3ce\ub3cf\ub3d0\ub3d1\ub3d2\ub3d3\ub3d4\ub3d5\ub3d6\ub3d7\ub3d8\ub3d9\ub3da\ub3db\ub3dc\ub3dd\ub3de\ub3df\ub3e0\ub3e1\ub3e2\ub3e3\ub3e4\ub3e5\ub3e6\ub3e7\ub3e8\ub3e9\ub3ea\ub3eb\ub3ec\ub3ed\ub3ee\ub3ef\ub3f0\ub3f1\ub3f2\ub3f3\ub3f4\ub3f5\ub3f6\ub3f7\ub3f8\ub3f9\ub3fa\ub3fb\ub3fc\ub3fd\ub3fe\ub3ff\ub400\ub401\ub402\ub403\ub404\ub405\ub406\ub407\ub408\ub409\ub40a\ub40b\ub40c\ub40d\ub40e\ub40f\ub410\ub411\ub412\ub413\ub414\ub415\ub416\ub417\ub418\ub419\ub41a\ub41b\ub41c\ub41d\ub41e\ub41f\ub420\ub421\ub422\ub423\ub424\ub425\ub426\ub427\ub428\ub429\ub42a\ub42b\ub42c\ub42d\ub42e\ub42f\ub430\ub431\ub432\ub433\ub434\ub435\ub436\ub437\ub438\ub439\ub43a\ub43b\ub43c\ub43d\ub43e\ub43f\ub440\ub441\ub442\ub443\ub444\ub445\ub446\ub447\ub448\ub449\ub44a\ub44b\ub44c\ub44d\ub44e\ub44f\ub450\ub451\ub452\ub453\ub454\ub455\ub456\ub457\ub458\ub459\ub45a\ub45b\ub45c\ub45d\ub45e\ub45f\ub460\ub461\ub462\ub463\ub464\ub465\ub466\ub467\ub468\ub469\ub46a\ub46b\ub46c\ub46d\ub46e\ub46f\ub470\ub471\ub472\ub473\ub474\ub475\ub476\ub477\ub478\ub479\ub47a\ub47b\ub47c\ub47d\ub47e\ub47f\ub480\ub481\ub482\ub483\ub484\ub485\ub486\ub487\ub488\ub489\ub48a\ub48b\ub48c\ub48d\ub48e\ub48f\ub490\ub491\ub492\ub493\ub494\ub495\ub496\ub497\ub498\ub499\ub49a\ub49b\ub49c\ub49d\ub49e\ub49f\ub4a0\ub4a1\ub4a2\ub4a3\ub4a4\ub4a5\ub4a6\ub4a7\ub4a8\ub4a9\ub4aa\ub4ab\ub4ac\ub4ad\ub4ae\ub4af\ub4b0\ub4b1\ub4b2\ub4b3\ub4b4\ub4b5\ub4b6\ub4b7\ub4b8\ub4b9\ub4ba\ub4bb\ub4bc\ub4bd\ub4be\ub4bf\ub4c0\ub4c1\ub4c2\ub4c3\ub4c4\ub4c5\ub4c6\ub4c7\ub4c8\ub4c9\ub4ca\ub4cb\ub4cc\ub4cd\ub4ce\ub4cf\ub4d0\ub4d1\ub4d2\ub4d3\ub4d4\ub4d5\ub4d6\ub4d7\ub4d8\ub4d9\ub4da\ub4db\ub4dc\ub4dd\ub4de\ub4df\ub4e0\ub4e1\ub4e2\ub4e3\ub4e4\ub4e5\ub4e6\ub4e7\ub4e8\ub4e9\ub4ea\ub4eb\ub4ec\ub4ed\ub4ee\ub4ef\ub4f0\ub4f1\ub4f2\ub4f3\ub4f4\ub4f5\ub4f6\ub4f7\ub4f8\ub4f9\ub4fa\ub4fb\ub4fc\ub4fd\ub4fe\ub4ff\ub500\ub501\ub502\ub503\ub504\ub505\ub506\ub507\ub508\ub509\ub50a\ub50b\ub50c\ub50d\ub50e\ub50f\ub510\ub511\ub512\ub513\ub514\ub515\ub516\ub517\ub518\ub519\ub51a\ub51b\ub51c\ub51d\ub51e\ub51f\ub520\ub521\ub522\ub523\ub524\ub525\ub526\ub527\ub528\ub529\ub52a\ub52b\ub52c\ub52d\ub52e\ub52f\ub530\ub531\ub532\ub533\ub534\ub535\ub536\ub537\ub538\ub539\ub53a\ub53b\ub53c\ub53d\ub53e\ub53f\ub540\ub541\ub542\ub543\ub544\ub545\ub546\ub547\ub548\ub549\ub54a\ub54b\ub54c\ub54d\ub54e\ub54f\ub550\ub551\ub552\ub553\ub554\ub555\ub556\ub557\ub558\ub559\ub55a\ub55b\ub55c\ub55d\ub55e\ub55f\ub560\ub561\ub562\ub563\ub564\ub565\ub566\ub567\ub568\ub569\ub56a\ub56b\ub56c\ub56d\ub56e\ub56f\ub570\ub571\ub572\ub573\ub574\ub575\ub576\ub577\ub578\ub579\ub57a\ub57b\ub57c\ub57d\ub57e\ub57f\ub580\ub581\ub582\ub583\ub584\ub585\ub586\ub587\ub588\ub589\ub58a\ub58b\ub58c\ub58d\ub58e\ub58f\ub590\ub591\ub592\ub593\ub594\ub595\ub596\ub597\ub598\ub599\ub59a\ub59b\ub59c\ub59d\ub59e\ub59f\ub5a0\ub5a1\ub5a2\ub5a3\ub5a4\ub5a5\ub5a6\ub5a7\ub5a8\ub5a9\ub5aa\ub5ab\ub5ac\ub5ad\ub5ae\ub5af\ub5b0\ub5b1\ub5b2\ub5b3\ub5b4\ub5b5\ub5b6\ub5b7\ub5b8\ub5b9\ub5ba\ub5bb\ub5bc\ub5bd\ub5be\ub5bf\ub5c0\ub5c1\ub5c2\ub5c3\ub5c4\ub5c5\ub5c6\ub5c7\ub5c8\ub5c9\ub5ca\ub5cb\ub5cc\ub5cd\ub5ce\ub5cf\ub5d0\ub5d1\ub5d2\ub5d3\ub5d4\ub5d5\ub5d6\ub5d7\ub5d8\ub5d9\ub5da\ub5db\ub5dc\ub5dd\ub5de\ub5df\ub5e0\ub5e1\ub5e2\ub5e3\ub5e4\ub5e5\ub5e6\ub5e7\ub5e8\ub5e9\ub5ea\ub5eb\ub5ec\ub5ed\ub5ee\ub5ef\ub5f0\ub5f1\ub5f2\ub5f3\ub5f4\ub5f5\ub5f6\ub5f7\ub5f8\ub5f9\ub5fa\ub5fb\ub5fc\ub5fd\ub5fe\ub5ff\ub600\ub601\ub602\ub603\ub604\ub605\ub606\ub607\ub608\ub609\ub60a\ub60b\ub60c\ub60d\ub60e\ub60f\ub610\ub611\ub612\ub613\ub614\ub615\ub616\ub617\ub618\ub619\ub61a\ub61b\ub61c\ub61d\ub61e\ub61f\ub620\ub621\ub622\ub623\ub624\ub625\ub626\ub627\ub628\ub629\ub62a\ub62b\ub62c\ub62d\ub62e\ub62f\ub630\ub631\ub632\ub633\ub634\ub635\ub636\ub637\ub638\ub639\ub63a\ub63b\ub63c\ub63d\ub63e\ub63f\ub640\ub641\ub642\ub643\ub644\ub645\ub646\ub647\ub648\ub649\ub64a\ub64b\ub64c\ub64d\ub64e\ub64f\ub650\ub651\ub652\ub653\ub654\ub655\ub656\ub657\ub658\ub659\ub65a\ub65b\ub65c\ub65d\ub65e\ub65f\ub660\ub661\ub662\ub663\ub664\ub665\ub666\ub667\ub668\ub669\ub66a\ub66b\ub66c\ub66d\ub66e\ub66f\ub670\ub671\ub672\ub673\ub674\ub675\ub676\ub677\ub678\ub679\ub67a\ub67b\ub67c\ub67d\ub67e\ub67f\ub680\ub681\ub682\ub683\ub684\ub685\ub686\ub687\ub688\ub689\ub68a\ub68b\ub68c\ub68d\ub68e\ub68f\ub690\ub691\ub692\ub693\ub694\ub695\ub696\ub697\ub698\ub699\ub69a\ub69b\ub69c\ub69d\ub69e\ub69f\ub6a0\ub6a1\ub6a2\ub6a3\ub6a4\ub6a5\ub6a6\ub6a7\ub6a8\ub6a9\ub6aa\ub6ab\ub6ac\ub6ad\ub6ae\ub6af\ub6b0\ub6b1\ub6b2\ub6b3\ub6b4\ub6b5\ub6b6\ub6b7\ub6b8\ub6b9\ub6ba\ub6bb\ub6bc\ub6bd\ub6be\ub6bf\ub6c0\ub6c1\ub6c2\ub6c3\ub6c4\ub6c5\ub6c6\ub6c7\ub6c8\ub6c9\ub6ca\ub6cb\ub6cc\ub6cd\ub6ce\ub6cf\ub6d0\ub6d1\ub6d2\ub6d3\ub6d4\ub6d5\ub6d6\ub6d7\ub6d8\ub6d9\ub6da\ub6db\ub6dc\ub6dd\ub6de\ub6df\ub6e0\ub6e1\ub6e2\ub6e3\ub6e4\ub6e5\ub6e6\ub6e7\ub6e8\ub6e9\ub6ea\ub6eb\ub6ec\ub6ed\ub6ee\ub6ef\ub6f0\ub6f1\ub6f2\ub6f3\ub6f4\ub6f5\ub6f6\ub6f7\ub6f8\ub6f9\ub6fa\ub6fb\ub6fc\ub6fd\ub6fe\ub6ff\ub700\ub701\ub702\ub703\ub704\ub705\ub706\ub707\ub708\ub709\ub70a\ub70b\ub70c\ub70d\ub70e\ub70f\ub710\ub711\ub712\ub713\ub714\ub715\ub716\ub717\ub718\ub719\ub71a\ub71b\ub71c\ub71d\ub71e\ub71f\ub720\ub721\ub722\ub723\ub724\ub725\ub726\ub727\ub728\ub729\ub72a\ub72b\ub72c\ub72d\ub72e\ub72f\ub730\ub731\ub732\ub733\ub734\ub735\ub736\ub737\ub738\ub739\ub73a\ub73b\ub73c\ub73d\ub73e\ub73f\ub740\ub741\ub742\ub743\ub744\ub745\ub746\ub747\ub748\ub749\ub74a\ub74b\ub74c\ub74d\ub74e\ub74f\ub750\ub751\ub752\ub753\ub754\ub755\ub756\ub757\ub758\ub759\ub75a\ub75b\ub75c\ub75d\ub75e\ub75f\ub760\ub761\ub762\ub763\ub764\ub765\ub766\ub767\ub768\ub769\ub76a\ub76b\ub76c\ub76d\ub76e\ub76f\ub770\ub771\ub772\ub773\ub774\ub775\ub776\ub777\ub778\ub779\ub77a\ub77b\ub77c\ub77d\ub77e\ub77f\ub780\ub781\ub782\ub783\ub784\ub785\ub786\ub787\ub788\ub789\ub78a\ub78b\ub78c\ub78d\ub78e\ub78f\ub790\ub791\ub792\ub793\ub794\ub795\ub796\ub797\ub798\ub799\ub79a\ub79b\ub79c\ub79d\ub79e\ub79f\ub7a0\ub7a1\ub7a2\ub7a3\ub7a4\ub7a5\ub7a6\ub7a7\ub7a8\ub7a9\ub7aa\ub7ab\ub7ac\ub7ad\ub7ae\ub7af\ub7b0\ub7b1\ub7b2\ub7b3\ub7b4\ub7b5\ub7b6\ub7b7\ub7b8\ub7b9\ub7ba\ub7bb\ub7bc\ub7bd\ub7be\ub7bf\ub7c0\ub7c1\ub7c2\ub7c3\ub7c4\ub7c5\ub7c6\ub7c7\ub7c8\ub7c9\ub7ca\ub7cb\ub7cc\ub7cd\ub7ce\ub7cf\ub7d0\ub7d1\ub7d2\ub7d3\ub7d4\ub7d5\ub7d6\ub7d7\ub7d8\ub7d9\ub7da\ub7db\ub7dc\ub7dd\ub7de\ub7df\ub7e0\ub7e1\ub7e2\ub7e3\ub7e4\ub7e5\ub7e6\ub7e7\ub7e8\ub7e9\ub7ea\ub7eb\ub7ec\ub7ed\ub7ee\ub7ef\ub7f0\ub7f1\ub7f2\ub7f3\ub7f4\ub7f5\ub7f6\ub7f7\ub7f8\ub7f9\ub7fa\ub7fb\ub7fc\ub7fd\ub7fe\ub7ff\ub800\ub801\ub802\ub803\ub804\ub805\ub806\ub807\ub808\ub809\ub80a\ub80b\ub80c\ub80d\ub80e\ub80f\ub810\ub811\ub812\ub813\ub814\ub815\ub816\ub817\ub818\ub819\ub81a\ub81b\ub81c\ub81d\ub81e\ub81f\ub820\ub821\ub822\ub823\ub824\ub825\ub826\ub827\ub828\ub829\ub82a\ub82b\ub82c\ub82d\ub82e\ub82f\ub830\ub831\ub832\ub833\ub834\ub835\ub836\ub837\ub838\ub839\ub83a\ub83b\ub83c\ub83d\ub83e\ub83f\ub840\ub841\ub842\ub843\ub844\ub845\ub846\ub847\ub848\ub849\ub84a\ub84b\ub84c\ub84d\ub84e\ub84f\ub850\ub851\ub852\ub853\ub854\ub855\ub856\ub857\ub858\ub859\ub85a\ub85b\ub85c\ub85d\ub85e\ub85f\ub860\ub861\ub862\ub863\ub864\ub865\ub866\ub867\ub868\ub869\ub86a\ub86b\ub86c\ub86d\ub86e\ub86f\ub870\ub871\ub872\ub873\ub874\ub875\ub876\ub877\ub878\ub879\ub87a\ub87b\ub87c\ub87d\ub87e\ub87f\ub880\ub881\ub882\ub883\ub884\ub885\ub886\ub887\ub888\ub889\ub88a\ub88b\ub88c\ub88d\ub88e\ub88f\ub890\ub891\ub892\ub893\ub894\ub895\ub896\ub897\ub898\ub899\ub89a\ub89b\ub89c\ub89d\ub89e\ub89f\ub8a0\ub8a1\ub8a2\ub8a3\ub8a4\ub8a5\ub8a6\ub8a7\ub8a8\ub8a9\ub8aa\ub8ab\ub8ac\ub8ad\ub8ae\ub8af\ub8b0\ub8b1\ub8b2\ub8b3\ub8b4\ub8b5\ub8b6\ub8b7\ub8b8\ub8b9\ub8ba\ub8bb\ub8bc\ub8bd\ub8be\ub8bf\ub8c0\ub8c1\ub8c2\ub8c3\ub8c4\ub8c5\ub8c6\ub8c7\ub8c8\ub8c9\ub8ca\ub8cb\ub8cc\ub8cd\ub8ce\ub8cf\ub8d0\ub8d1\ub8d2\ub8d3\ub8d4\ub8d5\ub8d6\ub8d7\ub8d8\ub8d9\ub8da\ub8db\ub8dc\ub8dd\ub8de\ub8df\ub8e0\ub8e1\ub8e2\ub8e3\ub8e4\ub8e5\ub8e6\ub8e7\ub8e8\ub8e9\ub8ea\ub8eb\ub8ec\ub8ed\ub8ee\ub8ef\ub8f0\ub8f1\ub8f2\ub8f3\ub8f4\ub8f5\ub8f6\ub8f7\ub8f8\ub8f9\ub8fa\ub8fb\ub8fc\ub8fd\ub8fe\ub8ff\ub900\ub901\ub902\ub903\ub904\ub905\ub906\ub907\ub908\ub909\ub90a\ub90b\ub90c\ub90d\ub90e\ub90f\ub910\ub911\ub912\ub913\ub914\ub915\ub916\ub917\ub918\ub919\ub91a\ub91b\ub91c\ub91d\ub91e\ub91f\ub920\ub921\ub922\ub923\ub924\ub925\ub926\ub927\ub928\ub929\ub92a\ub92b\ub92c\ub92d\ub92e\ub92f\ub930\ub931\ub932\ub933\ub934\ub935\ub936\ub937\ub938\ub939\ub93a\ub93b\ub93c\ub93d\ub93e\ub93f\ub940\ub941\ub942\ub943\ub944\ub945\ub946\ub947\ub948\ub949\ub94a\ub94b\ub94c\ub94d\ub94e\ub94f\ub950\ub951\ub952\ub953\ub954\ub955\ub956\ub957\ub958\ub959\ub95a\ub95b\ub95c\ub95d\ub95e\ub95f\ub960\ub961\ub962\ub963\ub964\ub965\ub966\ub967\ub968\ub969\ub96a\ub96b\ub96c\ub96d\ub96e\ub96f\ub970\ub971\ub972\ub973\ub974\ub975\ub976\ub977\ub978\ub979\ub97a\ub97b\ub97c\ub97d\ub97e\ub97f\ub980\ub981\ub982\ub983\ub984\ub985\ub986\ub987\ub988\ub989\ub98a\ub98b\ub98c\ub98d\ub98e\ub98f\ub990\ub991\ub992\ub993\ub994\ub995\ub996\ub997\ub998\ub999\ub99a\ub99b\ub99c\ub99d\ub99e\ub99f\ub9a0\ub9a1\ub9a2\ub9a3\ub9a4\ub9a5\ub9a6\ub9a7\ub9a8\ub9a9\ub9aa\ub9ab\ub9ac\ub9ad\ub9ae\ub9af\ub9b0\ub9b1\ub9b2\ub9b3\ub9b4\ub9b5\ub9b6\ub9b7\ub9b8\ub9b9\ub9ba\ub9bb\ub9bc\ub9bd\ub9be\ub9bf\ub9c0\ub9c1\ub9c2\ub9c3\ub9c4\ub9c5\ub9c6\ub9c7\ub9c8\ub9c9\ub9ca\ub9cb\ub9cc\ub9cd\ub9ce\ub9cf\ub9d0\ub9d1\ub9d2\ub9d3\ub9d4\ub9d5\ub9d6\ub9d7\ub9d8\ub9d9\ub9da\ub9db\ub9dc\ub9dd\ub9de\ub9df\ub9e0\ub9e1\ub9e2\ub9e3\ub9e4\ub9e5\ub9e6\ub9e7\ub9e8\ub9e9\ub9ea\ub9eb\ub9ec\ub9ed\ub9ee\ub9ef\ub9f0\ub9f1\ub9f2\ub9f3\ub9f4\ub9f5\ub9f6\ub9f7\ub9f8\ub9f9\ub9fa\ub9fb\ub9fc\ub9fd\ub9fe\ub9ff\uba00\uba01\uba02\uba03\uba04\uba05\uba06\uba07\uba08\uba09\uba0a\uba0b\uba0c\uba0d\uba0e\uba0f\uba10\uba11\uba12\uba13\uba14\uba15\uba16\uba17\uba18\uba19\uba1a\uba1b\uba1c\uba1d\uba1e\uba1f\uba20\uba21\uba22\uba23\uba24\uba25\uba26\uba27\uba28\uba29\uba2a\uba2b\uba2c\uba2d\uba2e\uba2f\uba30\uba31\uba32\uba33\uba34\uba35\uba36\uba37\uba38\uba39\uba3a\uba3b\uba3c\uba3d\uba3e\uba3f\uba40\uba41\uba42\uba43\uba44\uba45\uba46\uba47\uba48\uba49\uba4a\uba4b\uba4c\uba4d\uba4e\uba4f\uba50\uba51\uba52\uba53\uba54\uba55\uba56\uba57\uba58\uba59\uba5a\uba5b\uba5c\uba5d\uba5e\uba5f\uba60\uba61\uba62\uba63\uba64\uba65\uba66\uba67\uba68\uba69\uba6a\uba6b\uba6c\uba6d\uba6e\uba6f\uba70\uba71\uba72\uba73\uba74\uba75\uba76\uba77\uba78\uba79\uba7a\uba7b\uba7c\uba7d\uba7e\uba7f\uba80\uba81\uba82\uba83\uba84\uba85\uba86\uba87\uba88\uba89\uba8a\uba8b\uba8c\uba8d\uba8e\uba8f\uba90\uba91\uba92\uba93\uba94\uba95\uba96\uba97\uba98\uba99\uba9a\uba9b\uba9c\uba9d\uba9e\uba9f\ubaa0\ubaa1\ubaa2\ubaa3\ubaa4\ubaa5\ubaa6\ubaa7\ubaa8\ubaa9\ubaaa\ubaab\ubaac\ubaad\ubaae\ubaaf\ubab0\ubab1\ubab2\ubab3\ubab4\ubab5\ubab6\ubab7\ubab8\ubab9\ubaba\ubabb\ubabc\ubabd\ubabe\ubabf\ubac0\ubac1\ubac2\ubac3\ubac4\ubac5\ubac6\ubac7\ubac8\ubac9\ubaca\ubacb\ubacc\ubacd\ubace\ubacf\ubad0\ubad1\ubad2\ubad3\ubad4\ubad5\ubad6\ubad7\ubad8\ubad9\ubada\ubadb\ubadc\ubadd\ubade\ubadf\ubae0\ubae1\ubae2\ubae3\ubae4\ubae5\ubae6\ubae7\ubae8\ubae9\ubaea\ubaeb\ubaec\ubaed\ubaee\ubaef\ubaf0\ubaf1\ubaf2\ubaf3\ubaf4\ubaf5\ubaf6\ubaf7\ubaf8\ubaf9\ubafa\ubafb\ubafc\ubafd\ubafe\ubaff\ubb00\ubb01\ubb02\ubb03\ubb04\ubb05\ubb06\ubb07\ubb08\ubb09\ubb0a\ubb0b\ubb0c\ubb0d\ubb0e\ubb0f\ubb10\ubb11\ubb12\ubb13\ubb14\ubb15\ubb16\ubb17\ubb18\ubb19\ubb1a\ubb1b\ubb1c\ubb1d\ubb1e\ubb1f\ubb20\ubb21\ubb22\ubb23\ubb24\ubb25\ubb26\ubb27\ubb28\ubb29\ubb2a\ubb2b\ubb2c\ubb2d\ubb2e\ubb2f\ubb30\ubb31\ubb32\ubb33\ubb34\ubb35\ubb36\ubb37\ubb38\ubb39\ubb3a\ubb3b\ubb3c\ubb3d\ubb3e\ubb3f\ubb40\ubb41\ubb42\ubb43\ubb44\ubb45\ubb46\ubb47\ubb48\ubb49\ubb4a\ubb4b\ubb4c\ubb4d\ubb4e\ubb4f\ubb50\ubb51\ubb52\ubb53\ubb54\ubb55\ubb56\ubb57\ubb58\ubb59\ubb5a\ubb5b\ubb5c\ubb5d\ubb5e\ubb5f\ubb60\ubb61\ubb62\ubb63\ubb64\ubb65\ubb66\ubb67\ubb68\ubb69\ubb6a\ubb6b\ubb6c\ubb6d\ubb6e\ubb6f\ubb70\ubb71\ubb72\ubb73\ubb74\ubb75\ubb76\ubb77\ubb78\ubb79\ubb7a\ubb7b\ubb7c\ubb7d\ubb7e\ubb7f\ubb80\ubb81\ubb82\ubb83\ubb84\ubb85\ubb86\ubb87\ubb88\ubb89\ubb8a\ubb8b\ubb8c\ubb8d\ubb8e\ubb8f\ubb90\ubb91\ubb92\ubb93\ubb94\ubb95\ubb96\ubb97\ubb98\ubb99\ubb9a\ubb9b\ubb9c\ubb9d\ubb9e\ubb9f\ubba0\ubba1\ubba2\ubba3\ubba4\ubba5\ubba6\ubba7\ubba8\ubba9\ubbaa\ubbab\ubbac\ubbad\ubbae\ubbaf\ubbb0\ubbb1\ubbb2\ubbb3\ubbb4\ubbb5\ubbb6\ubbb7\ubbb8\ubbb9\ubbba\ubbbb\ubbbc\ubbbd\ubbbe\ubbbf\ubbc0\ubbc1\ubbc2\ubbc3\ubbc4\ubbc5\ubbc6\ubbc7\ubbc8\ubbc9\ubbca\ubbcb\ubbcc\ubbcd\ubbce\ubbcf\ubbd0\ubbd1\ubbd2\ubbd3\ubbd4\ubbd5\ubbd6\ubbd7\ubbd8\ubbd9\ubbda\ubbdb\ubbdc\ubbdd\ubbde\ubbdf\ubbe0\ubbe1\ubbe2\ubbe3\ubbe4\ubbe5\ubbe6\ubbe7\ubbe8\ubbe9\ubbea\ubbeb\ubbec\ubbed\ubbee\ubbef\ubbf0\ubbf1\ubbf2\ubbf3\ubbf4\ubbf5\ubbf6\ubbf7\ubbf8\ubbf9\ubbfa\ubbfb\ubbfc\ubbfd\ubbfe\ubbff\ubc00\ubc01\ubc02\ubc03\ubc04\ubc05\ubc06\ubc07\ubc08\ubc09\ubc0a\ubc0b\ubc0c\ubc0d\ubc0e\ubc0f\ubc10\ubc11\ubc12\ubc13\ubc14\ubc15\ubc16\ubc17\ubc18\ubc19\ubc1a\ubc1b\ubc1c\ubc1d\ubc1e\ubc1f\ubc20\ubc21\ubc22\ubc23\ubc24\ubc25\ubc26\ubc27\ubc28\ubc29\ubc2a\ubc2b\ubc2c\ubc2d\ubc2e\ubc2f\ubc30\ubc31\ubc32\ubc33\ubc34\ubc35\ubc36\ubc37\ubc38\ubc39\ubc3a\ubc3b\ubc3c\ubc3d\ubc3e\ubc3f\ubc40\ubc41\ubc42\ubc43\ubc44\ubc45\ubc46\ubc47\ubc48\ubc49\ubc4a\ubc4b\ubc4c\ubc4d\ubc4e\ubc4f\ubc50\ubc51\ubc52\ubc53\ubc54\ubc55\ubc56\ubc57\ubc58\ubc59\ubc5a\ubc5b\ubc5c\ubc5d\ubc5e\ubc5f\ubc60\ubc61\ubc62\ubc63\ubc64\ubc65\ubc66\ubc67\ubc68\ubc69\ubc6a\ubc6b\ubc6c\ubc6d\ubc6e\ubc6f\ubc70\ubc71\ubc72\ubc73\ubc74\ubc75\ubc76\ubc77\ubc78\ubc79\ubc7a\ubc7b\ubc7c\ubc7d\ubc7e\ubc7f\ubc80\ubc81\ubc82\ubc83\ubc84\ubc85\ubc86\ubc87\ubc88\ubc89\ubc8a\ubc8b\ubc8c\ubc8d\ubc8e\ubc8f\ubc90\ubc91\ubc92\ubc93\ubc94\ubc95\ubc96\ubc97\ubc98\ubc99\ubc9a\ubc9b\ubc9c\ubc9d\ubc9e\ubc9f\ubca0\ubca1\ubca2\ubca3\ubca4\ubca5\ubca6\ubca7\ubca8\ubca9\ubcaa\ubcab\ubcac\ubcad\ubcae\ubcaf\ubcb0\ubcb1\ubcb2\ubcb3\ubcb4\ubcb5\ubcb6\ubcb7\ubcb8\ubcb9\ubcba\ubcbb\ubcbc\ubcbd\ubcbe\ubcbf\ubcc0\ubcc1\ubcc2\ubcc3\ubcc4\ubcc5\ubcc6\ubcc7\ubcc8\ubcc9\ubcca\ubccb\ubccc\ubccd\ubcce\ubccf\ubcd0\ubcd1\ubcd2\ubcd3\ubcd4\ubcd5\ubcd6\ubcd7\ubcd8\ubcd9\ubcda\ubcdb\ubcdc\ubcdd\ubcde\ubcdf\ubce0\ubce1\ubce2\ubce3\ubce4\ubce5\ubce6\ubce7\ubce8\ubce9\ubcea\ubceb\ubcec\ubced\ubcee\ubcef\ubcf0\ubcf1\ubcf2\ubcf3\ubcf4\ubcf5\ubcf6\ubcf7\ubcf8\ubcf9\ubcfa\ubcfb\ubcfc\ubcfd\ubcfe\ubcff\ubd00\ubd01\ubd02\ubd03\ubd04\ubd05\ubd06\ubd07\ubd08\ubd09\ubd0a\ubd0b\ubd0c\ubd0d\ubd0e\ubd0f\ubd10\ubd11\ubd12\ubd13\ubd14\ubd15\ubd16\ubd17\ubd18\ubd19\ubd1a\ubd1b\ubd1c\ubd1d\ubd1e\ubd1f\ubd20\ubd21\ubd22\ubd23\ubd24\ubd25\ubd26\ubd27\ubd28\ubd29\ubd2a\ubd2b\ubd2c\ubd2d\ubd2e\ubd2f\ubd30\ubd31\ubd32\ubd33\ubd34\ubd35\ubd36\ubd37\ubd38\ubd39\ubd3a\ubd3b\ubd3c\ubd3d\ubd3e\ubd3f\ubd40\ubd41\ubd42\ubd43\ubd44\ubd45\ubd46\ubd47\ubd48\ubd49\ubd4a\ubd4b\ubd4c\ubd4d\ubd4e\ubd4f\ubd50\ubd51\ubd52\ubd53\ubd54\ubd55\ubd56\ubd57\ubd58\ubd59\ubd5a\ubd5b\ubd5c\ubd5d\ubd5e\ubd5f\ubd60\ubd61\ubd62\ubd63\ubd64\ubd65\ubd66\ubd67\ubd68\ubd69\ubd6a\ubd6b\ubd6c\ubd6d\ubd6e\ubd6f\ubd70\ubd71\ubd72\ubd73\ubd74\ubd75\ubd76\ubd77\ubd78\ubd79\ubd7a\ubd7b\ubd7c\ubd7d\ubd7e\ubd7f\ubd80\ubd81\ubd82\ubd83\ubd84\ubd85\ubd86\ubd87\ubd88\ubd89\ubd8a\ubd8b\ubd8c\ubd8d\ubd8e\ubd8f\ubd90\ubd91\ubd92\ubd93\ubd94\ubd95\ubd96\ubd97\ubd98\ubd99\ubd9a\ubd9b\ubd9c\ubd9d\ubd9e\ubd9f\ubda0\ubda1\ubda2\ubda3\ubda4\ubda5\ubda6\ubda7\ubda8\ubda9\ubdaa\ubdab\ubdac\ubdad\ubdae\ubdaf\ubdb0\ubdb1\ubdb2\ubdb3\ubdb4\ubdb5\ubdb6\ubdb7\ubdb8\ubdb9\ubdba\ubdbb\ubdbc\ubdbd\ubdbe\ubdbf\ubdc0\ubdc1\ubdc2\ubdc3\ubdc4\ubdc5\ubdc6\ubdc7\ubdc8\ubdc9\ubdca\ubdcb\ubdcc\ubdcd\ubdce\ubdcf\ubdd0\ubdd1\ubdd2\ubdd3\ubdd4\ubdd5\ubdd6\ubdd7\ubdd8\ubdd9\ubdda\ubddb\ubddc\ubddd\ubdde\ubddf\ubde0\ubde1\ubde2\ubde3\ubde4\ubde5\ubde6\ubde7\ubde8\ubde9\ubdea\ubdeb\ubdec\ubded\ubdee\ubdef\ubdf0\ubdf1\ubdf2\ubdf3\ubdf4\ubdf5\ubdf6\ubdf7\ubdf8\ubdf9\ubdfa\ubdfb\ubdfc\ubdfd\ubdfe\ubdff\ube00\ube01\ube02\ube03\ube04\ube05\ube06\ube07\ube08\ube09\ube0a\ube0b\ube0c\ube0d\ube0e\ube0f\ube10\ube11\ube12\ube13\ube14\ube15\ube16\ube17\ube18\ube19\ube1a\ube1b\ube1c\ube1d\ube1e\ube1f\ube20\ube21\ube22\ube23\ube24\ube25\ube26\ube27\ube28\ube29\ube2a\ube2b\ube2c\ube2d\ube2e\ube2f\ube30\ube31\ube32\ube33\ube34\ube35\ube36\ube37\ube38\ube39\ube3a\ube3b\ube3c\ube3d\ube3e\ube3f\ube40\ube41\ube42\ube43\ube44\ube45\ube46\ube47\ube48\ube49\ube4a\ube4b\ube4c\ube4d\ube4e\ube4f\ube50\ube51\ube52\ube53\ube54\ube55\ube56\ube57\ube58\ube59\ube5a\ube5b\ube5c\ube5d\ube5e\ube5f\ube60\ube61\ube62\ube63\ube64\ube65\ube66\ube67\ube68\ube69\ube6a\ube6b\ube6c\ube6d\ube6e\ube6f\ube70\ube71\ube72\ube73\ube74\ube75\ube76\ube77\ube78\ube79\ube7a\ube7b\ube7c\ube7d\ube7e\ube7f\ube80\ube81\ube82\ube83\ube84\ube85\ube86\ube87\ube88\ube89\ube8a\ube8b\ube8c\ube8d\ube8e\ube8f\ube90\ube91\ube92\ube93\ube94\ube95\ube96\ube97\ube98\ube99\ube9a\ube9b\ube9c\ube9d\ube9e\ube9f\ubea0\ubea1\ubea2\ubea3\ubea4\ubea5\ubea6\ubea7\ubea8\ubea9\ubeaa\ubeab\ubeac\ubead\ubeae\ubeaf\ubeb0\ubeb1\ubeb2\ubeb3\ubeb4\ubeb5\ubeb6\ubeb7\ubeb8\ubeb9\ubeba\ubebb\ubebc\ubebd\ubebe\ubebf\ubec0\ubec1\ubec2\ubec3\ubec4\ubec5\ubec6\ubec7\ubec8\ubec9\ubeca\ubecb\ubecc\ubecd\ubece\ubecf\ubed0\ubed1\ubed2\ubed3\ubed4\ubed5\ubed6\ubed7\ubed8\ubed9\ubeda\ubedb\ubedc\ubedd\ubede\ubedf\ubee0\ubee1\ubee2\ubee3\ubee4\ubee5\ubee6\ubee7\ubee8\ubee9\ubeea\ubeeb\ubeec\ubeed\ubeee\ubeef\ubef0\ubef1\ubef2\ubef3\ubef4\ubef5\ubef6\ubef7\ubef8\ubef9\ubefa\ubefb\ubefc\ubefd\ubefe\ubeff\ubf00\ubf01\ubf02\ubf03\ubf04\ubf05\ubf06\ubf07\ubf08\ubf09\ubf0a\ubf0b\ubf0c\ubf0d\ubf0e\ubf0f\ubf10\ubf11\ubf12\ubf13\ubf14\ubf15\ubf16\ubf17\ubf18\ubf19\ubf1a\ubf1b\ubf1c\ubf1d\ubf1e\ubf1f\ubf20\ubf21\ubf22\ubf23\ubf24\ubf25\ubf26\ubf27\ubf28\ubf29\ubf2a\ubf2b\ubf2c\ubf2d\ubf2e\ubf2f\ubf30\ubf31\ubf32\ubf33\ubf34\ubf35\ubf36\ubf37\ubf38\ubf39\ubf3a\ubf3b\ubf3c\ubf3d\ubf3e\ubf3f\ubf40\ubf41\ubf42\ubf43\ubf44\ubf45\ubf46\ubf47\ubf48\ubf49\ubf4a\ubf4b\ubf4c\ubf4d\ubf4e\ubf4f\ubf50\ubf51\ubf52\ubf53\ubf54\ubf55\ubf56\ubf57\ubf58\ubf59\ubf5a\ubf5b\ubf5c\ubf5d\ubf5e\ubf5f\ubf60\ubf61\ubf62\ubf63\ubf64\ubf65\ubf66\ubf67\ubf68\ubf69\ubf6a\ubf6b\ubf6c\ubf6d\ubf6e\ubf6f\ubf70\ubf71\ubf72\ubf73\ubf74\ubf75\ubf76\ubf77\ubf78\ubf79\ubf7a\ubf7b\ubf7c\ubf7d\ubf7e\ubf7f\ubf80\ubf81\ubf82\ubf83\ubf84\ubf85\ubf86\ubf87\ubf88\ubf89\ubf8a\ubf8b\ubf8c\ubf8d\ubf8e\ubf8f\ubf90\ubf91\ubf92\ubf93\ubf94\ubf95\ubf96\ubf97\ubf98\ubf99\ubf9a\ubf9b\ubf9c\ubf9d\ubf9e\ubf9f\ubfa0\ubfa1\ubfa2\ubfa3\ubfa4\ubfa5\ubfa6\ubfa7\ubfa8\ubfa9\ubfaa\ubfab\ubfac\ubfad\ubfae\ubfaf\ubfb0\ubfb1\ubfb2\ubfb3\ubfb4\ubfb5\ubfb6\ubfb7\ubfb8\ubfb9\ubfba\ubfbb\ubfbc\ubfbd\ubfbe\ubfbf\ubfc0\ubfc1\ubfc2\ubfc3\ubfc4\ubfc5\ubfc6\ubfc7\ubfc8\ubfc9\ubfca\ubfcb\ubfcc\ubfcd\ubfce\ubfcf\ubfd0\ubfd1\ubfd2\ubfd3\ubfd4\ubfd5\ubfd6\ubfd7\ubfd8\ubfd9\ubfda\ubfdb\ubfdc\ubfdd\ubfde\ubfdf\ubfe0\ubfe1\ubfe2\ubfe3\ubfe4\ubfe5\ubfe6\ubfe7\ubfe8\ubfe9\ubfea\ubfeb\ubfec\ubfed\ubfee\ubfef\ubff0\ubff1\ubff2\ubff3\ubff4\ubff5\ubff6\ubff7\ubff8\ubff9\ubffa\ubffb\ubffc\ubffd\ubffe\ubfff\uc000\uc001\uc002\uc003\uc004\uc005\uc006\uc007\uc008\uc009\uc00a\uc00b\uc00c\uc00d\uc00e\uc00f\uc010\uc011\uc012\uc013\uc014\uc015\uc016\uc017\uc018\uc019\uc01a\uc01b\uc01c\uc01d\uc01e\uc01f\uc020\uc021\uc022\uc023\uc024\uc025\uc026\uc027\uc028\uc029\uc02a\uc02b\uc02c\uc02d\uc02e\uc02f\uc030\uc031\uc032\uc033\uc034\uc035\uc036\uc037\uc038\uc039\uc03a\uc03b\uc03c\uc03d\uc03e\uc03f\uc040\uc041\uc042\uc043\uc044\uc045\uc046\uc047\uc048\uc049\uc04a\uc04b\uc04c\uc04d\uc04e\uc04f\uc050\uc051\uc052\uc053\uc054\uc055\uc056\uc057\uc058\uc059\uc05a\uc05b\uc05c\uc05d\uc05e\uc05f\uc060\uc061\uc062\uc063\uc064\uc065\uc066\uc067\uc068\uc069\uc06a\uc06b\uc06c\uc06d\uc06e\uc06f\uc070\uc071\uc072\uc073\uc074\uc075\uc076\uc077\uc078\uc079\uc07a\uc07b\uc07c\uc07d\uc07e\uc07f\uc080\uc081\uc082\uc083\uc084\uc085\uc086\uc087\uc088\uc089\uc08a\uc08b\uc08c\uc08d\uc08e\uc08f\uc090\uc091\uc092\uc093\uc094\uc095\uc096\uc097\uc098\uc099\uc09a\uc09b\uc09c\uc09d\uc09e\uc09f\uc0a0\uc0a1\uc0a2\uc0a3\uc0a4\uc0a5\uc0a6\uc0a7\uc0a8\uc0a9\uc0aa\uc0ab\uc0ac\uc0ad\uc0ae\uc0af\uc0b0\uc0b1\uc0b2\uc0b3\uc0b4\uc0b5\uc0b6\uc0b7\uc0b8\uc0b9\uc0ba\uc0bb\uc0bc\uc0bd\uc0be\uc0bf\uc0c0\uc0c1\uc0c2\uc0c3\uc0c4\uc0c5\uc0c6\uc0c7\uc0c8\uc0c9\uc0ca\uc0cb\uc0cc\uc0cd\uc0ce\uc0cf\uc0d0\uc0d1\uc0d2\uc0d3\uc0d4\uc0d5\uc0d6\uc0d7\uc0d8\uc0d9\uc0da\uc0db\uc0dc\uc0dd\uc0de\uc0df\uc0e0\uc0e1\uc0e2\uc0e3\uc0e4\uc0e5\uc0e6\uc0e7\uc0e8\uc0e9\uc0ea\uc0eb\uc0ec\uc0ed\uc0ee\uc0ef\uc0f0\uc0f1\uc0f2\uc0f3\uc0f4\uc0f5\uc0f6\uc0f7\uc0f8\uc0f9\uc0fa\uc0fb\uc0fc\uc0fd\uc0fe\uc0ff\uc100\uc101\uc102\uc103\uc104\uc105\uc106\uc107\uc108\uc109\uc10a\uc10b\uc10c\uc10d\uc10e\uc10f\uc110\uc111\uc112\uc113\uc114\uc115\uc116\uc117\uc118\uc119\uc11a\uc11b\uc11c\uc11d\uc11e\uc11f\uc120\uc121\uc122\uc123\uc124\uc125\uc126\uc127\uc128\uc129\uc12a\uc12b\uc12c\uc12d\uc12e\uc12f\uc130\uc131\uc132\uc133\uc134\uc135\uc136\uc137\uc138\uc139\uc13a\uc13b\uc13c\uc13d\uc13e\uc13f\uc140\uc141\uc142\uc143\uc144\uc145\uc146\uc147\uc148\uc149\uc14a\uc14b\uc14c\uc14d\uc14e\uc14f\uc150\uc151\uc152\uc153\uc154\uc155\uc156\uc157\uc158\uc159\uc15a\uc15b\uc15c\uc15d\uc15e\uc15f\uc160\uc161\uc162\uc163\uc164\uc165\uc166\uc167\uc168\uc169\uc16a\uc16b\uc16c\uc16d\uc16e\uc16f\uc170\uc171\uc172\uc173\uc174\uc175\uc176\uc177\uc178\uc179\uc17a\uc17b\uc17c\uc17d\uc17e\uc17f\uc180\uc181\uc182\uc183\uc184\uc185\uc186\uc187\uc188\uc189\uc18a\uc18b\uc18c\uc18d\uc18e\uc18f\uc190\uc191\uc192\uc193\uc194\uc195\uc196\uc197\uc198\uc199\uc19a\uc19b\uc19c\uc19d\uc19e\uc19f\uc1a0\uc1a1\uc1a2\uc1a3\uc1a4\uc1a5\uc1a6\uc1a7\uc1a8\uc1a9\uc1aa\uc1ab\uc1ac\uc1ad\uc1ae\uc1af\uc1b0\uc1b1\uc1b2\uc1b3\uc1b4\uc1b5\uc1b6\uc1b7\uc1b8\uc1b9\uc1ba\uc1bb\uc1bc\uc1bd\uc1be\uc1bf\uc1c0\uc1c1\uc1c2\uc1c3\uc1c4\uc1c5\uc1c6\uc1c7\uc1c8\uc1c9\uc1ca\uc1cb\uc1cc\uc1cd\uc1ce\uc1cf\uc1d0\uc1d1\uc1d2\uc1d3\uc1d4\uc1d5\uc1d6\uc1d7\uc1d8\uc1d9\uc1da\uc1db\uc1dc\uc1dd\uc1de\uc1df\uc1e0\uc1e1\uc1e2\uc1e3\uc1e4\uc1e5\uc1e6\uc1e7\uc1e8\uc1e9\uc1ea\uc1eb\uc1ec\uc1ed\uc1ee\uc1ef\uc1f0\uc1f1\uc1f2\uc1f3\uc1f4\uc1f5\uc1f6\uc1f7\uc1f8\uc1f9\uc1fa\uc1fb\uc1fc\uc1fd\uc1fe\uc1ff\uc200\uc201\uc202\uc203\uc204\uc205\uc206\uc207\uc208\uc209\uc20a\uc20b\uc20c\uc20d\uc20e\uc20f\uc210\uc211\uc212\uc213\uc214\uc215\uc216\uc217\uc218\uc219\uc21a\uc21b\uc21c\uc21d\uc21e\uc21f\uc220\uc221\uc222\uc223\uc224\uc225\uc226\uc227\uc228\uc229\uc22a\uc22b\uc22c\uc22d\uc22e\uc22f\uc230\uc231\uc232\uc233\uc234\uc235\uc236\uc237\uc238\uc239\uc23a\uc23b\uc23c\uc23d\uc23e\uc23f\uc240\uc241\uc242\uc243\uc244\uc245\uc246\uc247\uc248\uc249\uc24a\uc24b\uc24c\uc24d\uc24e\uc24f\uc250\uc251\uc252\uc253\uc254\uc255\uc256\uc257\uc258\uc259\uc25a\uc25b\uc25c\uc25d\uc25e\uc25f\uc260\uc261\uc262\uc263\uc264\uc265\uc266\uc267\uc268\uc269\uc26a\uc26b\uc26c\uc26d\uc26e\uc26f\uc270\uc271\uc272\uc273\uc274\uc275\uc276\uc277\uc278\uc279\uc27a\uc27b\uc27c\uc27d\uc27e\uc27f\uc280\uc281\uc282\uc283\uc284\uc285\uc286\uc287\uc288\uc289\uc28a\uc28b\uc28c\uc28d\uc28e\uc28f\uc290\uc291\uc292\uc293\uc294\uc295\uc296\uc297\uc298\uc299\uc29a\uc29b\uc29c\uc29d\uc29e\uc29f\uc2a0\uc2a1\uc2a2\uc2a3\uc2a4\uc2a5\uc2a6\uc2a7\uc2a8\uc2a9\uc2aa\uc2ab\uc2ac\uc2ad\uc2ae\uc2af\uc2b0\uc2b1\uc2b2\uc2b3\uc2b4\uc2b5\uc2b6\uc2b7\uc2b8\uc2b9\uc2ba\uc2bb\uc2bc\uc2bd\uc2be\uc2bf\uc2c0\uc2c1\uc2c2\uc2c3\uc2c4\uc2c5\uc2c6\uc2c7\uc2c8\uc2c9\uc2ca\uc2cb\uc2cc\uc2cd\uc2ce\uc2cf\uc2d0\uc2d1\uc2d2\uc2d3\uc2d4\uc2d5\uc2d6\uc2d7\uc2d8\uc2d9\uc2da\uc2db\uc2dc\uc2dd\uc2de\uc2df\uc2e0\uc2e1\uc2e2\uc2e3\uc2e4\uc2e5\uc2e6\uc2e7\uc2e8\uc2e9\uc2ea\uc2eb\uc2ec\uc2ed\uc2ee\uc2ef\uc2f0\uc2f1\uc2f2\uc2f3\uc2f4\uc2f5\uc2f6\uc2f7\uc2f8\uc2f9\uc2fa\uc2fb\uc2fc\uc2fd\uc2fe\uc2ff\uc300\uc301\uc302\uc303\uc304\uc305\uc306\uc307\uc308\uc309\uc30a\uc30b\uc30c\uc30d\uc30e\uc30f\uc310\uc311\uc312\uc313\uc314\uc315\uc316\uc317\uc318\uc319\uc31a\uc31b\uc31c\uc31d\uc31e\uc31f\uc320\uc321\uc322\uc323\uc324\uc325\uc326\uc327\uc328\uc329\uc32a\uc32b\uc32c\uc32d\uc32e\uc32f\uc330\uc331\uc332\uc333\uc334\uc335\uc336\uc337\uc338\uc339\uc33a\uc33b\uc33c\uc33d\uc33e\uc33f\uc340\uc341\uc342\uc343\uc344\uc345\uc346\uc347\uc348\uc349\uc34a\uc34b\uc34c\uc34d\uc34e\uc34f\uc350\uc351\uc352\uc353\uc354\uc355\uc356\uc357\uc358\uc359\uc35a\uc35b\uc35c\uc35d\uc35e\uc35f\uc360\uc361\uc362\uc363\uc364\uc365\uc366\uc367\uc368\uc369\uc36a\uc36b\uc36c\uc36d\uc36e\uc36f\uc370\uc371\uc372\uc373\uc374\uc375\uc376\uc377\uc378\uc379\uc37a\uc37b\uc37c\uc37d\uc37e\uc37f\uc380\uc381\uc382\uc383\uc384\uc385\uc386\uc387\uc388\uc389\uc38a\uc38b\uc38c\uc38d\uc38e\uc38f\uc390\uc391\uc392\uc393\uc394\uc395\uc396\uc397\uc398\uc399\uc39a\uc39b\uc39c\uc39d\uc39e\uc39f\uc3a0\uc3a1\uc3a2\uc3a3\uc3a4\uc3a5\uc3a6\uc3a7\uc3a8\uc3a9\uc3aa\uc3ab\uc3ac\uc3ad\uc3ae\uc3af\uc3b0\uc3b1\uc3b2\uc3b3\uc3b4\uc3b5\uc3b6\uc3b7\uc3b8\uc3b9\uc3ba\uc3bb\uc3bc\uc3bd\uc3be\uc3bf\uc3c0\uc3c1\uc3c2\uc3c3\uc3c4\uc3c5\uc3c6\uc3c7\uc3c8\uc3c9\uc3ca\uc3cb\uc3cc\uc3cd\uc3ce\uc3cf\uc3d0\uc3d1\uc3d2\uc3d3\uc3d4\uc3d5\uc3d6\uc3d7\uc3d8\uc3d9\uc3da\uc3db\uc3dc\uc3dd\uc3de\uc3df\uc3e0\uc3e1\uc3e2\uc3e3\uc3e4\uc3e5\uc3e6\uc3e7\uc3e8\uc3e9\uc3ea\uc3eb\uc3ec\uc3ed\uc3ee\uc3ef\uc3f0\uc3f1\uc3f2\uc3f3\uc3f4\uc3f5\uc3f6\uc3f7\uc3f8\uc3f9\uc3fa\uc3fb\uc3fc\uc3fd\uc3fe\uc3ff\uc400\uc401\uc402\uc403\uc404\uc405\uc406\uc407\uc408\uc409\uc40a\uc40b\uc40c\uc40d\uc40e\uc40f\uc410\uc411\uc412\uc413\uc414\uc415\uc416\uc417\uc418\uc419\uc41a\uc41b\uc41c\uc41d\uc41e\uc41f\uc420\uc421\uc422\uc423\uc424\uc425\uc426\uc427\uc428\uc429\uc42a\uc42b\uc42c\uc42d\uc42e\uc42f\uc430\uc431\uc432\uc433\uc434\uc435\uc436\uc437\uc438\uc439\uc43a\uc43b\uc43c\uc43d\uc43e\uc43f\uc440\uc441\uc442\uc443\uc444\uc445\uc446\uc447\uc448\uc449\uc44a\uc44b\uc44c\uc44d\uc44e\uc44f\uc450\uc451\uc452\uc453\uc454\uc455\uc456\uc457\uc458\uc459\uc45a\uc45b\uc45c\uc45d\uc45e\uc45f\uc460\uc461\uc462\uc463\uc464\uc465\uc466\uc467\uc468\uc469\uc46a\uc46b\uc46c\uc46d\uc46e\uc46f\uc470\uc471\uc472\uc473\uc474\uc475\uc476\uc477\uc478\uc479\uc47a\uc47b\uc47c\uc47d\uc47e\uc47f\uc480\uc481\uc482\uc483\uc484\uc485\uc486\uc487\uc488\uc489\uc48a\uc48b\uc48c\uc48d\uc48e\uc48f\uc490\uc491\uc492\uc493\uc494\uc495\uc496\uc497\uc498\uc499\uc49a\uc49b\uc49c\uc49d\uc49e\uc49f\uc4a0\uc4a1\uc4a2\uc4a3\uc4a4\uc4a5\uc4a6\uc4a7\uc4a8\uc4a9\uc4aa\uc4ab\uc4ac\uc4ad\uc4ae\uc4af\uc4b0\uc4b1\uc4b2\uc4b3\uc4b4\uc4b5\uc4b6\uc4b7\uc4b8\uc4b9\uc4ba\uc4bb\uc4bc\uc4bd\uc4be\uc4bf\uc4c0\uc4c1\uc4c2\uc4c3\uc4c4\uc4c5\uc4c6\uc4c7\uc4c8\uc4c9\uc4ca\uc4cb\uc4cc\uc4cd\uc4ce\uc4cf\uc4d0\uc4d1\uc4d2\uc4d3\uc4d4\uc4d5\uc4d6\uc4d7\uc4d8\uc4d9\uc4da\uc4db\uc4dc\uc4dd\uc4de\uc4df\uc4e0\uc4e1\uc4e2\uc4e3\uc4e4\uc4e5\uc4e6\uc4e7\uc4e8\uc4e9\uc4ea\uc4eb\uc4ec\uc4ed\uc4ee\uc4ef\uc4f0\uc4f1\uc4f2\uc4f3\uc4f4\uc4f5\uc4f6\uc4f7\uc4f8\uc4f9\uc4fa\uc4fb\uc4fc\uc4fd\uc4fe\uc4ff\uc500\uc501\uc502\uc503\uc504\uc505\uc506\uc507\uc508\uc509\uc50a\uc50b\uc50c\uc50d\uc50e\uc50f\uc510\uc511\uc512\uc513\uc514\uc515\uc516\uc517\uc518\uc519\uc51a\uc51b\uc51c\uc51d\uc51e\uc51f\uc520\uc521\uc522\uc523\uc524\uc525\uc526\uc527\uc528\uc529\uc52a\uc52b\uc52c\uc52d\uc52e\uc52f\uc530\uc531\uc532\uc533\uc534\uc535\uc536\uc537\uc538\uc539\uc53a\uc53b\uc53c\uc53d\uc53e\uc53f\uc540\uc541\uc542\uc543\uc544\uc545\uc546\uc547\uc548\uc549\uc54a\uc54b\uc54c\uc54d\uc54e\uc54f\uc550\uc551\uc552\uc553\uc554\uc555\uc556\uc557\uc558\uc559\uc55a\uc55b\uc55c\uc55d\uc55e\uc55f\uc560\uc561\uc562\uc563\uc564\uc565\uc566\uc567\uc568\uc569\uc56a\uc56b\uc56c\uc56d\uc56e\uc56f\uc570\uc571\uc572\uc573\uc574\uc575\uc576\uc577\uc578\uc579\uc57a\uc57b\uc57c\uc57d\uc57e\uc57f\uc580\uc581\uc582\uc583\uc584\uc585\uc586\uc587\uc588\uc589\uc58a\uc58b\uc58c\uc58d\uc58e\uc58f\uc590\uc591\uc592\uc593\uc594\uc595\uc596\uc597\uc598\uc599\uc59a\uc59b\uc59c\uc59d\uc59e\uc59f\uc5a0\uc5a1\uc5a2\uc5a3\uc5a4\uc5a5\uc5a6\uc5a7\uc5a8\uc5a9\uc5aa\uc5ab\uc5ac\uc5ad\uc5ae\uc5af\uc5b0\uc5b1\uc5b2\uc5b3\uc5b4\uc5b5\uc5b6\uc5b7\uc5b8\uc5b9\uc5ba\uc5bb\uc5bc\uc5bd\uc5be\uc5bf\uc5c0\uc5c1\uc5c2\uc5c3\uc5c4\uc5c5\uc5c6\uc5c7\uc5c8\uc5c9\uc5ca\uc5cb\uc5cc\uc5cd\uc5ce\uc5cf\uc5d0\uc5d1\uc5d2\uc5d3\uc5d4\uc5d5\uc5d6\uc5d7\uc5d8\uc5d9\uc5da\uc5db\uc5dc\uc5dd\uc5de\uc5df\uc5e0\uc5e1\uc5e2\uc5e3\uc5e4\uc5e5\uc5e6\uc5e7\uc5e8\uc5e9\uc5ea\uc5eb\uc5ec\uc5ed\uc5ee\uc5ef\uc5f0\uc5f1\uc5f2\uc5f3\uc5f4\uc5f5\uc5f6\uc5f7\uc5f8\uc5f9\uc5fa\uc5fb\uc5fc\uc5fd\uc5fe\uc5ff\uc600\uc601\uc602\uc603\uc604\uc605\uc606\uc607\uc608\uc609\uc60a\uc60b\uc60c\uc60d\uc60e\uc60f\uc610\uc611\uc612\uc613\uc614\uc615\uc616\uc617\uc618\uc619\uc61a\uc61b\uc61c\uc61d\uc61e\uc61f\uc620\uc621\uc622\uc623\uc624\uc625\uc626\uc627\uc628\uc629\uc62a\uc62b\uc62c\uc62d\uc62e\uc62f\uc630\uc631\uc632\uc633\uc634\uc635\uc636\uc637\uc638\uc639\uc63a\uc63b\uc63c\uc63d\uc63e\uc63f\uc640\uc641\uc642\uc643\uc644\uc645\uc646\uc647\uc648\uc649\uc64a\uc64b\uc64c\uc64d\uc64e\uc64f\uc650\uc651\uc652\uc653\uc654\uc655\uc656\uc657\uc658\uc659\uc65a\uc65b\uc65c\uc65d\uc65e\uc65f\uc660\uc661\uc662\uc663\uc664\uc665\uc666\uc667\uc668\uc669\uc66a\uc66b\uc66c\uc66d\uc66e\uc66f\uc670\uc671\uc672\uc673\uc674\uc675\uc676\uc677\uc678\uc679\uc67a\uc67b\uc67c\uc67d\uc67e\uc67f\uc680\uc681\uc682\uc683\uc684\uc685\uc686\uc687\uc688\uc689\uc68a\uc68b\uc68c\uc68d\uc68e\uc68f\uc690\uc691\uc692\uc693\uc694\uc695\uc696\uc697\uc698\uc699\uc69a\uc69b\uc69c\uc69d\uc69e\uc69f\uc6a0\uc6a1\uc6a2\uc6a3\uc6a4\uc6a5\uc6a6\uc6a7\uc6a8\uc6a9\uc6aa\uc6ab\uc6ac\uc6ad\uc6ae\uc6af\uc6b0\uc6b1\uc6b2\uc6b3\uc6b4\uc6b5\uc6b6\uc6b7\uc6b8\uc6b9\uc6ba\uc6bb\uc6bc\uc6bd\uc6be\uc6bf\uc6c0\uc6c1\uc6c2\uc6c3\uc6c4\uc6c5\uc6c6\uc6c7\uc6c8\uc6c9\uc6ca\uc6cb\uc6cc\uc6cd\uc6ce\uc6cf\uc6d0\uc6d1\uc6d2\uc6d3\uc6d4\uc6d5\uc6d6\uc6d7\uc6d8\uc6d9\uc6da\uc6db\uc6dc\uc6dd\uc6de\uc6df\uc6e0\uc6e1\uc6e2\uc6e3\uc6e4\uc6e5\uc6e6\uc6e7\uc6e8\uc6e9\uc6ea\uc6eb\uc6ec\uc6ed\uc6ee\uc6ef\uc6f0\uc6f1\uc6f2\uc6f3\uc6f4\uc6f5\uc6f6\uc6f7\uc6f8\uc6f9\uc6fa\uc6fb\uc6fc\uc6fd\uc6fe\uc6ff\uc700\uc701\uc702\uc703\uc704\uc705\uc706\uc707\uc708\uc709\uc70a\uc70b\uc70c\uc70d\uc70e\uc70f\uc710\uc711\uc712\uc713\uc714\uc715\uc716\uc717\uc718\uc719\uc71a\uc71b\uc71c\uc71d\uc71e\uc71f\uc720\uc721\uc722\uc723\uc724\uc725\uc726\uc727\uc728\uc729\uc72a\uc72b\uc72c\uc72d\uc72e\uc72f\uc730\uc731\uc732\uc733\uc734\uc735\uc736\uc737\uc738\uc739\uc73a\uc73b\uc73c\uc73d\uc73e\uc73f\uc740\uc741\uc742\uc743\uc744\uc745\uc746\uc747\uc748\uc749\uc74a\uc74b\uc74c\uc74d\uc74e\uc74f\uc750\uc751\uc752\uc753\uc754\uc755\uc756\uc757\uc758\uc759\uc75a\uc75b\uc75c\uc75d\uc75e\uc75f\uc760\uc761\uc762\uc763\uc764\uc765\uc766\uc767\uc768\uc769\uc76a\uc76b\uc76c\uc76d\uc76e\uc76f\uc770\uc771\uc772\uc773\uc774\uc775\uc776\uc777\uc778\uc779\uc77a\uc77b\uc77c\uc77d\uc77e\uc77f\uc780\uc781\uc782\uc783\uc784\uc785\uc786\uc787\uc788\uc789\uc78a\uc78b\uc78c\uc78d\uc78e\uc78f\uc790\uc791\uc792\uc793\uc794\uc795\uc796\uc797\uc798\uc799\uc79a\uc79b\uc79c\uc79d\uc79e\uc79f\uc7a0\uc7a1\uc7a2\uc7a3\uc7a4\uc7a5\uc7a6\uc7a7\uc7a8\uc7a9\uc7aa\uc7ab\uc7ac\uc7ad\uc7ae\uc7af\uc7b0\uc7b1\uc7b2\uc7b3\uc7b4\uc7b5\uc7b6\uc7b7\uc7b8\uc7b9\uc7ba\uc7bb\uc7bc\uc7bd\uc7be\uc7bf\uc7c0\uc7c1\uc7c2\uc7c3\uc7c4\uc7c5\uc7c6\uc7c7\uc7c8\uc7c9\uc7ca\uc7cb\uc7cc\uc7cd\uc7ce\uc7cf\uc7d0\uc7d1\uc7d2\uc7d3\uc7d4\uc7d5\uc7d6\uc7d7\uc7d8\uc7d9\uc7da\uc7db\uc7dc\uc7dd\uc7de\uc7df\uc7e0\uc7e1\uc7e2\uc7e3\uc7e4\uc7e5\uc7e6\uc7e7\uc7e8\uc7e9\uc7ea\uc7eb\uc7ec\uc7ed\uc7ee\uc7ef\uc7f0\uc7f1\uc7f2\uc7f3\uc7f4\uc7f5\uc7f6\uc7f7\uc7f8\uc7f9\uc7fa\uc7fb\uc7fc\uc7fd\uc7fe\uc7ff\uc800\uc801\uc802\uc803\uc804\uc805\uc806\uc807\uc808\uc809\uc80a\uc80b\uc80c\uc80d\uc80e\uc80f\uc810\uc811\uc812\uc813\uc814\uc815\uc816\uc817\uc818\uc819\uc81a\uc81b\uc81c\uc81d\uc81e\uc81f\uc820\uc821\uc822\uc823\uc824\uc825\uc826\uc827\uc828\uc829\uc82a\uc82b\uc82c\uc82d\uc82e\uc82f\uc830\uc831\uc832\uc833\uc834\uc835\uc836\uc837\uc838\uc839\uc83a\uc83b\uc83c\uc83d\uc83e\uc83f\uc840\uc841\uc842\uc843\uc844\uc845\uc846\uc847\uc848\uc849\uc84a\uc84b\uc84c\uc84d\uc84e\uc84f\uc850\uc851\uc852\uc853\uc854\uc855\uc856\uc857\uc858\uc859\uc85a\uc85b\uc85c\uc85d\uc85e\uc85f\uc860\uc861\uc862\uc863\uc864\uc865\uc866\uc867\uc868\uc869\uc86a\uc86b\uc86c\uc86d\uc86e\uc86f\uc870\uc871\uc872\uc873\uc874\uc875\uc876\uc877\uc878\uc879\uc87a\uc87b\uc87c\uc87d\uc87e\uc87f\uc880\uc881\uc882\uc883\uc884\uc885\uc886\uc887\uc888\uc889\uc88a\uc88b\uc88c\uc88d\uc88e\uc88f\uc890\uc891\uc892\uc893\uc894\uc895\uc896\uc897\uc898\uc899\uc89a\uc89b\uc89c\uc89d\uc89e\uc89f\uc8a0\uc8a1\uc8a2\uc8a3\uc8a4\uc8a5\uc8a6\uc8a7\uc8a8\uc8a9\uc8aa\uc8ab\uc8ac\uc8ad\uc8ae\uc8af\uc8b0\uc8b1\uc8b2\uc8b3\uc8b4\uc8b5\uc8b6\uc8b7\uc8b8\uc8b9\uc8ba\uc8bb\uc8bc\uc8bd\uc8be\uc8bf\uc8c0\uc8c1\uc8c2\uc8c3\uc8c4\uc8c5\uc8c6\uc8c7\uc8c8\uc8c9\uc8ca\uc8cb\uc8cc\uc8cd\uc8ce\uc8cf\uc8d0\uc8d1\uc8d2\uc8d3\uc8d4\uc8d5\uc8d6\uc8d7\uc8d8\uc8d9\uc8da\uc8db\uc8dc\uc8dd\uc8de\uc8df\uc8e0\uc8e1\uc8e2\uc8e3\uc8e4\uc8e5\uc8e6\uc8e7\uc8e8\uc8e9\uc8ea\uc8eb\uc8ec\uc8ed\uc8ee\uc8ef\uc8f0\uc8f1\uc8f2\uc8f3\uc8f4\uc8f5\uc8f6\uc8f7\uc8f8\uc8f9\uc8fa\uc8fb\uc8fc\uc8fd\uc8fe\uc8ff\uc900\uc901\uc902\uc903\uc904\uc905\uc906\uc907\uc908\uc909\uc90a\uc90b\uc90c\uc90d\uc90e\uc90f\uc910\uc911\uc912\uc913\uc914\uc915\uc916\uc917\uc918\uc919\uc91a\uc91b\uc91c\uc91d\uc91e\uc91f\uc920\uc921\uc922\uc923\uc924\uc925\uc926\uc927\uc928\uc929\uc92a\uc92b\uc92c\uc92d\uc92e\uc92f\uc930\uc931\uc932\uc933\uc934\uc935\uc936\uc937\uc938\uc939\uc93a\uc93b\uc93c\uc93d\uc93e\uc93f\uc940\uc941\uc942\uc943\uc944\uc945\uc946\uc947\uc948\uc949\uc94a\uc94b\uc94c\uc94d\uc94e\uc94f\uc950\uc951\uc952\uc953\uc954\uc955\uc956\uc957\uc958\uc959\uc95a\uc95b\uc95c\uc95d\uc95e\uc95f\uc960\uc961\uc962\uc963\uc964\uc965\uc966\uc967\uc968\uc969\uc96a\uc96b\uc96c\uc96d\uc96e\uc96f\uc970\uc971\uc972\uc973\uc974\uc975\uc976\uc977\uc978\uc979\uc97a\uc97b\uc97c\uc97d\uc97e\uc97f\uc980\uc981\uc982\uc983\uc984\uc985\uc986\uc987\uc988\uc989\uc98a\uc98b\uc98c\uc98d\uc98e\uc98f\uc990\uc991\uc992\uc993\uc994\uc995\uc996\uc997\uc998\uc999\uc99a\uc99b\uc99c\uc99d\uc99e\uc99f\uc9a0\uc9a1\uc9a2\uc9a3\uc9a4\uc9a5\uc9a6\uc9a7\uc9a8\uc9a9\uc9aa\uc9ab\uc9ac\uc9ad\uc9ae\uc9af\uc9b0\uc9b1\uc9b2\uc9b3\uc9b4\uc9b5\uc9b6\uc9b7\uc9b8\uc9b9\uc9ba\uc9bb\uc9bc\uc9bd\uc9be\uc9bf\uc9c0\uc9c1\uc9c2\uc9c3\uc9c4\uc9c5\uc9c6\uc9c7\uc9c8\uc9c9\uc9ca\uc9cb\uc9cc\uc9cd\uc9ce\uc9cf\uc9d0\uc9d1\uc9d2\uc9d3\uc9d4\uc9d5\uc9d6\uc9d7\uc9d8\uc9d9\uc9da\uc9db\uc9dc\uc9dd\uc9de\uc9df\uc9e0\uc9e1\uc9e2\uc9e3\uc9e4\uc9e5\uc9e6\uc9e7\uc9e8\uc9e9\uc9ea\uc9eb\uc9ec\uc9ed\uc9ee\uc9ef\uc9f0\uc9f1\uc9f2\uc9f3\uc9f4\uc9f5\uc9f6\uc9f7\uc9f8\uc9f9\uc9fa\uc9fb\uc9fc\uc9fd\uc9fe\uc9ff\uca00\uca01\uca02\uca03\uca04\uca05\uca06\uca07\uca08\uca09\uca0a\uca0b\uca0c\uca0d\uca0e\uca0f\uca10\uca11\uca12\uca13\uca14\uca15\uca16\uca17\uca18\uca19\uca1a\uca1b\uca1c\uca1d\uca1e\uca1f\uca20\uca21\uca22\uca23\uca24\uca25\uca26\uca27\uca28\uca29\uca2a\uca2b\uca2c\uca2d\uca2e\uca2f\uca30\uca31\uca32\uca33\uca34\uca35\uca36\uca37\uca38\uca39\uca3a\uca3b\uca3c\uca3d\uca3e\uca3f\uca40\uca41\uca42\uca43\uca44\uca45\uca46\uca47\uca48\uca49\uca4a\uca4b\uca4c\uca4d\uca4e\uca4f\uca50\uca51\uca52\uca53\uca54\uca55\uca56\uca57\uca58\uca59\uca5a\uca5b\uca5c\uca5d\uca5e\uca5f\uca60\uca61\uca62\uca63\uca64\uca65\uca66\uca67\uca68\uca69\uca6a\uca6b\uca6c\uca6d\uca6e\uca6f\uca70\uca71\uca72\uca73\uca74\uca75\uca76\uca77\uca78\uca79\uca7a\uca7b\uca7c\uca7d\uca7e\uca7f\uca80\uca81\uca82\uca83\uca84\uca85\uca86\uca87\uca88\uca89\uca8a\uca8b\uca8c\uca8d\uca8e\uca8f\uca90\uca91\uca92\uca93\uca94\uca95\uca96\uca97\uca98\uca99\uca9a\uca9b\uca9c\uca9d\uca9e\uca9f\ucaa0\ucaa1\ucaa2\ucaa3\ucaa4\ucaa5\ucaa6\ucaa7\ucaa8\ucaa9\ucaaa\ucaab\ucaac\ucaad\ucaae\ucaaf\ucab0\ucab1\ucab2\ucab3\ucab4\ucab5\ucab6\ucab7\ucab8\ucab9\ucaba\ucabb\ucabc\ucabd\ucabe\ucabf\ucac0\ucac1\ucac2\ucac3\ucac4\ucac5\ucac6\ucac7\ucac8\ucac9\ucaca\ucacb\ucacc\ucacd\ucace\ucacf\ucad0\ucad1\ucad2\ucad3\ucad4\ucad5\ucad6\ucad7\ucad8\ucad9\ucada\ucadb\ucadc\ucadd\ucade\ucadf\ucae0\ucae1\ucae2\ucae3\ucae4\ucae5\ucae6\ucae7\ucae8\ucae9\ucaea\ucaeb\ucaec\ucaed\ucaee\ucaef\ucaf0\ucaf1\ucaf2\ucaf3\ucaf4\ucaf5\ucaf6\ucaf7\ucaf8\ucaf9\ucafa\ucafb\ucafc\ucafd\ucafe\ucaff\ucb00\ucb01\ucb02\ucb03\ucb04\ucb05\ucb06\ucb07\ucb08\ucb09\ucb0a\ucb0b\ucb0c\ucb0d\ucb0e\ucb0f\ucb10\ucb11\ucb12\ucb13\ucb14\ucb15\ucb16\ucb17\ucb18\ucb19\ucb1a\ucb1b\ucb1c\ucb1d\ucb1e\ucb1f\ucb20\ucb21\ucb22\ucb23\ucb24\ucb25\ucb26\ucb27\ucb28\ucb29\ucb2a\ucb2b\ucb2c\ucb2d\ucb2e\ucb2f\ucb30\ucb31\ucb32\ucb33\ucb34\ucb35\ucb36\ucb37\ucb38\ucb39\ucb3a\ucb3b\ucb3c\ucb3d\ucb3e\ucb3f\ucb40\ucb41\ucb42\ucb43\ucb44\ucb45\ucb46\ucb47\ucb48\ucb49\ucb4a\ucb4b\ucb4c\ucb4d\ucb4e\ucb4f\ucb50\ucb51\ucb52\ucb53\ucb54\ucb55\ucb56\ucb57\ucb58\ucb59\ucb5a\ucb5b\ucb5c\ucb5d\ucb5e\ucb5f\ucb60\ucb61\ucb62\ucb63\ucb64\ucb65\ucb66\ucb67\ucb68\ucb69\ucb6a\ucb6b\ucb6c\ucb6d\ucb6e\ucb6f\ucb70\ucb71\ucb72\ucb73\ucb74\ucb75\ucb76\ucb77\ucb78\ucb79\ucb7a\ucb7b\ucb7c\ucb7d\ucb7e\ucb7f\ucb80\ucb81\ucb82\ucb83\ucb84\ucb85\ucb86\ucb87\ucb88\ucb89\ucb8a\ucb8b\ucb8c\ucb8d\ucb8e\ucb8f\ucb90\ucb91\ucb92\ucb93\ucb94\ucb95\ucb96\ucb97\ucb98\ucb99\ucb9a\ucb9b\ucb9c\ucb9d\ucb9e\ucb9f\ucba0\ucba1\ucba2\ucba3\ucba4\ucba5\ucba6\ucba7\ucba8\ucba9\ucbaa\ucbab\ucbac\ucbad\ucbae\ucbaf\ucbb0\ucbb1\ucbb2\ucbb3\ucbb4\ucbb5\ucbb6\ucbb7\ucbb8\ucbb9\ucbba\ucbbb\ucbbc\ucbbd\ucbbe\ucbbf\ucbc0\ucbc1\ucbc2\ucbc3\ucbc4\ucbc5\ucbc6\ucbc7\ucbc8\ucbc9\ucbca\ucbcb\ucbcc\ucbcd\ucbce\ucbcf\ucbd0\ucbd1\ucbd2\ucbd3\ucbd4\ucbd5\ucbd6\ucbd7\ucbd8\ucbd9\ucbda\ucbdb\ucbdc\ucbdd\ucbde\ucbdf\ucbe0\ucbe1\ucbe2\ucbe3\ucbe4\ucbe5\ucbe6\ucbe7\ucbe8\ucbe9\ucbea\ucbeb\ucbec\ucbed\ucbee\ucbef\ucbf0\ucbf1\ucbf2\ucbf3\ucbf4\ucbf5\ucbf6\ucbf7\ucbf8\ucbf9\ucbfa\ucbfb\ucbfc\ucbfd\ucbfe\ucbff\ucc00\ucc01\ucc02\ucc03\ucc04\ucc05\ucc06\ucc07\ucc08\ucc09\ucc0a\ucc0b\ucc0c\ucc0d\ucc0e\ucc0f\ucc10\ucc11\ucc12\ucc13\ucc14\ucc15\ucc16\ucc17\ucc18\ucc19\ucc1a\ucc1b\ucc1c\ucc1d\ucc1e\ucc1f\ucc20\ucc21\ucc22\ucc23\ucc24\ucc25\ucc26\ucc27\ucc28\ucc29\ucc2a\ucc2b\ucc2c\ucc2d\ucc2e\ucc2f\ucc30\ucc31\ucc32\ucc33\ucc34\ucc35\ucc36\ucc37\ucc38\ucc39\ucc3a\ucc3b\ucc3c\ucc3d\ucc3e\ucc3f\ucc40\ucc41\ucc42\ucc43\ucc44\ucc45\ucc46\ucc47\ucc48\ucc49\ucc4a\ucc4b\ucc4c\ucc4d\ucc4e\ucc4f\ucc50\ucc51\ucc52\ucc53\ucc54\ucc55\ucc56\ucc57\ucc58\ucc59\ucc5a\ucc5b\ucc5c\ucc5d\ucc5e\ucc5f\ucc60\ucc61\ucc62\ucc63\ucc64\ucc65\ucc66\ucc67\ucc68\ucc69\ucc6a\ucc6b\ucc6c\ucc6d\ucc6e\ucc6f\ucc70\ucc71\ucc72\ucc73\ucc74\ucc75\ucc76\ucc77\ucc78\ucc79\ucc7a\ucc7b\ucc7c\ucc7d\ucc7e\ucc7f\ucc80\ucc81\ucc82\ucc83\ucc84\ucc85\ucc86\ucc87\ucc88\ucc89\ucc8a\ucc8b\ucc8c\ucc8d\ucc8e\ucc8f\ucc90\ucc91\ucc92\ucc93\ucc94\ucc95\ucc96\ucc97\ucc98\ucc99\ucc9a\ucc9b\ucc9c\ucc9d\ucc9e\ucc9f\ucca0\ucca1\ucca2\ucca3\ucca4\ucca5\ucca6\ucca7\ucca8\ucca9\uccaa\uccab\uccac\uccad\uccae\uccaf\uccb0\uccb1\uccb2\uccb3\uccb4\uccb5\uccb6\uccb7\uccb8\uccb9\uccba\uccbb\uccbc\uccbd\uccbe\uccbf\uccc0\uccc1\uccc2\uccc3\uccc4\uccc5\uccc6\uccc7\uccc8\uccc9\uccca\ucccb\ucccc\ucccd\uccce\ucccf\uccd0\uccd1\uccd2\uccd3\uccd4\uccd5\uccd6\uccd7\uccd8\uccd9\uccda\uccdb\uccdc\uccdd\uccde\uccdf\ucce0\ucce1\ucce2\ucce3\ucce4\ucce5\ucce6\ucce7\ucce8\ucce9\uccea\ucceb\uccec\ucced\uccee\uccef\uccf0\uccf1\uccf2\uccf3\uccf4\uccf5\uccf6\uccf7\uccf8\uccf9\uccfa\uccfb\uccfc\uccfd\uccfe\uccff\ucd00\ucd01\ucd02\ucd03\ucd04\ucd05\ucd06\ucd07\ucd08\ucd09\ucd0a\ucd0b\ucd0c\ucd0d\ucd0e\ucd0f\ucd10\ucd11\ucd12\ucd13\ucd14\ucd15\ucd16\ucd17\ucd18\ucd19\ucd1a\ucd1b\ucd1c\ucd1d\ucd1e\ucd1f\ucd20\ucd21\ucd22\ucd23\ucd24\ucd25\ucd26\ucd27\ucd28\ucd29\ucd2a\ucd2b\ucd2c\ucd2d\ucd2e\ucd2f\ucd30\ucd31\ucd32\ucd33\ucd34\ucd35\ucd36\ucd37\ucd38\ucd39\ucd3a\ucd3b\ucd3c\ucd3d\ucd3e\ucd3f\ucd40\ucd41\ucd42\ucd43\ucd44\ucd45\ucd46\ucd47\ucd48\ucd49\ucd4a\ucd4b\ucd4c\ucd4d\ucd4e\ucd4f\ucd50\ucd51\ucd52\ucd53\ucd54\ucd55\ucd56\ucd57\ucd58\ucd59\ucd5a\ucd5b\ucd5c\ucd5d\ucd5e\ucd5f\ucd60\ucd61\ucd62\ucd63\ucd64\ucd65\ucd66\ucd67\ucd68\ucd69\ucd6a\ucd6b\ucd6c\ucd6d\ucd6e\ucd6f\ucd70\ucd71\ucd72\ucd73\ucd74\ucd75\ucd76\ucd77\ucd78\ucd79\ucd7a\ucd7b\ucd7c\ucd7d\ucd7e\ucd7f\ucd80\ucd81\ucd82\ucd83\ucd84\ucd85\ucd86\ucd87\ucd88\ucd89\ucd8a\ucd8b\ucd8c\ucd8d\ucd8e\ucd8f\ucd90\ucd91\ucd92\ucd93\ucd94\ucd95\ucd96\ucd97\ucd98\ucd99\ucd9a\ucd9b\ucd9c\ucd9d\ucd9e\ucd9f\ucda0\ucda1\ucda2\ucda3\ucda4\ucda5\ucda6\ucda7\ucda8\ucda9\ucdaa\ucdab\ucdac\ucdad\ucdae\ucdaf\ucdb0\ucdb1\ucdb2\ucdb3\ucdb4\ucdb5\ucdb6\ucdb7\ucdb8\ucdb9\ucdba\ucdbb\ucdbc\ucdbd\ucdbe\ucdbf\ucdc0\ucdc1\ucdc2\ucdc3\ucdc4\ucdc5\ucdc6\ucdc7\ucdc8\ucdc9\ucdca\ucdcb\ucdcc\ucdcd\ucdce\ucdcf\ucdd0\ucdd1\ucdd2\ucdd3\ucdd4\ucdd5\ucdd6\ucdd7\ucdd8\ucdd9\ucdda\ucddb\ucddc\ucddd\ucdde\ucddf\ucde0\ucde1\ucde2\ucde3\ucde4\ucde5\ucde6\ucde7\ucde8\ucde9\ucdea\ucdeb\ucdec\ucded\ucdee\ucdef\ucdf0\ucdf1\ucdf2\ucdf3\ucdf4\ucdf5\ucdf6\ucdf7\ucdf8\ucdf9\ucdfa\ucdfb\ucdfc\ucdfd\ucdfe\ucdff\uce00\uce01\uce02\uce03\uce04\uce05\uce06\uce07\uce08\uce09\uce0a\uce0b\uce0c\uce0d\uce0e\uce0f\uce10\uce11\uce12\uce13\uce14\uce15\uce16\uce17\uce18\uce19\uce1a\uce1b\uce1c\uce1d\uce1e\uce1f\uce20\uce21\uce22\uce23\uce24\uce25\uce26\uce27\uce28\uce29\uce2a\uce2b\uce2c\uce2d\uce2e\uce2f\uce30\uce31\uce32\uce33\uce34\uce35\uce36\uce37\uce38\uce39\uce3a\uce3b\uce3c\uce3d\uce3e\uce3f\uce40\uce41\uce42\uce43\uce44\uce45\uce46\uce47\uce48\uce49\uce4a\uce4b\uce4c\uce4d\uce4e\uce4f\uce50\uce51\uce52\uce53\uce54\uce55\uce56\uce57\uce58\uce59\uce5a\uce5b\uce5c\uce5d\uce5e\uce5f\uce60\uce61\uce62\uce63\uce64\uce65\uce66\uce67\uce68\uce69\uce6a\uce6b\uce6c\uce6d\uce6e\uce6f\uce70\uce71\uce72\uce73\uce74\uce75\uce76\uce77\uce78\uce79\uce7a\uce7b\uce7c\uce7d\uce7e\uce7f\uce80\uce81\uce82\uce83\uce84\uce85\uce86\uce87\uce88\uce89\uce8a\uce8b\uce8c\uce8d\uce8e\uce8f\uce90\uce91\uce92\uce93\uce94\uce95\uce96\uce97\uce98\uce99\uce9a\uce9b\uce9c\uce9d\uce9e\uce9f\ucea0\ucea1\ucea2\ucea3\ucea4\ucea5\ucea6\ucea7\ucea8\ucea9\uceaa\uceab\uceac\ucead\uceae\uceaf\uceb0\uceb1\uceb2\uceb3\uceb4\uceb5\uceb6\uceb7\uceb8\uceb9\uceba\ucebb\ucebc\ucebd\ucebe\ucebf\ucec0\ucec1\ucec2\ucec3\ucec4\ucec5\ucec6\ucec7\ucec8\ucec9\uceca\ucecb\ucecc\ucecd\ucece\ucecf\uced0\uced1\uced2\uced3\uced4\uced5\uced6\uced7\uced8\uced9\uceda\ucedb\ucedc\ucedd\ucede\ucedf\ucee0\ucee1\ucee2\ucee3\ucee4\ucee5\ucee6\ucee7\ucee8\ucee9\uceea\uceeb\uceec\uceed\uceee\uceef\ucef0\ucef1\ucef2\ucef3\ucef4\ucef5\ucef6\ucef7\ucef8\ucef9\ucefa\ucefb\ucefc\ucefd\ucefe\uceff\ucf00\ucf01\ucf02\ucf03\ucf04\ucf05\ucf06\ucf07\ucf08\ucf09\ucf0a\ucf0b\ucf0c\ucf0d\ucf0e\ucf0f\ucf10\ucf11\ucf12\ucf13\ucf14\ucf15\ucf16\ucf17\ucf18\ucf19\ucf1a\ucf1b\ucf1c\ucf1d\ucf1e\ucf1f\ucf20\ucf21\ucf22\ucf23\ucf24\ucf25\ucf26\ucf27\ucf28\ucf29\ucf2a\ucf2b\ucf2c\ucf2d\ucf2e\ucf2f\ucf30\ucf31\ucf32\ucf33\ucf34\ucf35\ucf36\ucf37\ucf38\ucf39\ucf3a\ucf3b\ucf3c\ucf3d\ucf3e\ucf3f\ucf40\ucf41\ucf42\ucf43\ucf44\ucf45\ucf46\ucf47\ucf48\ucf49\ucf4a\ucf4b\ucf4c\ucf4d\ucf4e\ucf4f\ucf50\ucf51\ucf52\ucf53\ucf54\ucf55\ucf56\ucf57\ucf58\ucf59\ucf5a\ucf5b\ucf5c\ucf5d\ucf5e\ucf5f\ucf60\ucf61\ucf62\ucf63\ucf64\ucf65\ucf66\ucf67\ucf68\ucf69\ucf6a\ucf6b\ucf6c\ucf6d\ucf6e\ucf6f\ucf70\ucf71\ucf72\ucf73\ucf74\ucf75\ucf76\ucf77\ucf78\ucf79\ucf7a\ucf7b\ucf7c\ucf7d\ucf7e\ucf7f\ucf80\ucf81\ucf82\ucf83\ucf84\ucf85\ucf86\ucf87\ucf88\ucf89\ucf8a\ucf8b\ucf8c\ucf8d\ucf8e\ucf8f\ucf90\ucf91\ucf92\ucf93\ucf94\ucf95\ucf96\ucf97\ucf98\ucf99\ucf9a\ucf9b\ucf9c\ucf9d\ucf9e\ucf9f\ucfa0\ucfa1\ucfa2\ucfa3\ucfa4\ucfa5\ucfa6\ucfa7\ucfa8\ucfa9\ucfaa\ucfab\ucfac\ucfad\ucfae\ucfaf\ucfb0\ucfb1\ucfb2\ucfb3\ucfb4\ucfb5\ucfb6\ucfb7\ucfb8\ucfb9\ucfba\ucfbb\ucfbc\ucfbd\ucfbe\ucfbf\ucfc0\ucfc1\ucfc2\ucfc3\ucfc4\ucfc5\ucfc6\ucfc7\ucfc8\ucfc9\ucfca\ucfcb\ucfcc\ucfcd\ucfce\ucfcf\ucfd0\ucfd1\ucfd2\ucfd3\ucfd4\ucfd5\ucfd6\ucfd7\ucfd8\ucfd9\ucfda\ucfdb\ucfdc\ucfdd\ucfde\ucfdf\ucfe0\ucfe1\ucfe2\ucfe3\ucfe4\ucfe5\ucfe6\ucfe7\ucfe8\ucfe9\ucfea\ucfeb\ucfec\ucfed\ucfee\ucfef\ucff0\ucff1\ucff2\ucff3\ucff4\ucff5\ucff6\ucff7\ucff8\ucff9\ucffa\ucffb\ucffc\ucffd\ucffe\ucfff\ud000\ud001\ud002\ud003\ud004\ud005\ud006\ud007\ud008\ud009\ud00a\ud00b\ud00c\ud00d\ud00e\ud00f\ud010\ud011\ud012\ud013\ud014\ud015\ud016\ud017\ud018\ud019\ud01a\ud01b\ud01c\ud01d\ud01e\ud01f\ud020\ud021\ud022\ud023\ud024\ud025\ud026\ud027\ud028\ud029\ud02a\ud02b\ud02c\ud02d\ud02e\ud02f\ud030\ud031\ud032\ud033\ud034\ud035\ud036\ud037\ud038\ud039\ud03a\ud03b\ud03c\ud03d\ud03e\ud03f\ud040\ud041\ud042\ud043\ud044\ud045\ud046\ud047\ud048\ud049\ud04a\ud04b\ud04c\ud04d\ud04e\ud04f\ud050\ud051\ud052\ud053\ud054\ud055\ud056\ud057\ud058\ud059\ud05a\ud05b\ud05c\ud05d\ud05e\ud05f\ud060\ud061\ud062\ud063\ud064\ud065\ud066\ud067\ud068\ud069\ud06a\ud06b\ud06c\ud06d\ud06e\ud06f\ud070\ud071\ud072\ud073\ud074\ud075\ud076\ud077\ud078\ud079\ud07a\ud07b\ud07c\ud07d\ud07e\ud07f\ud080\ud081\ud082\ud083\ud084\ud085\ud086\ud087\ud088\ud089\ud08a\ud08b\ud08c\ud08d\ud08e\ud08f\ud090\ud091\ud092\ud093\ud094\ud095\ud096\ud097\ud098\ud099\ud09a\ud09b\ud09c\ud09d\ud09e\ud09f\ud0a0\ud0a1\ud0a2\ud0a3\ud0a4\ud0a5\ud0a6\ud0a7\ud0a8\ud0a9\ud0aa\ud0ab\ud0ac\ud0ad\ud0ae\ud0af\ud0b0\ud0b1\ud0b2\ud0b3\ud0b4\ud0b5\ud0b6\ud0b7\ud0b8\ud0b9\ud0ba\ud0bb\ud0bc\ud0bd\ud0be\ud0bf\ud0c0\ud0c1\ud0c2\ud0c3\ud0c4\ud0c5\ud0c6\ud0c7\ud0c8\ud0c9\ud0ca\ud0cb\ud0cc\ud0cd\ud0ce\ud0cf\ud0d0\ud0d1\ud0d2\ud0d3\ud0d4\ud0d5\ud0d6\ud0d7\ud0d8\ud0d9\ud0da\ud0db\ud0dc\ud0dd\ud0de\ud0df\ud0e0\ud0e1\ud0e2\ud0e3\ud0e4\ud0e5\ud0e6\ud0e7\ud0e8\ud0e9\ud0ea\ud0eb\ud0ec\ud0ed\ud0ee\ud0ef\ud0f0\ud0f1\ud0f2\ud0f3\ud0f4\ud0f5\ud0f6\ud0f7\ud0f8\ud0f9\ud0fa\ud0fb\ud0fc\ud0fd\ud0fe\ud0ff\ud100\ud101\ud102\ud103\ud104\ud105\ud106\ud107\ud108\ud109\ud10a\ud10b\ud10c\ud10d\ud10e\ud10f\ud110\ud111\ud112\ud113\ud114\ud115\ud116\ud117\ud118\ud119\ud11a\ud11b\ud11c\ud11d\ud11e\ud11f\ud120\ud121\ud122\ud123\ud124\ud125\ud126\ud127\ud128\ud129\ud12a\ud12b\ud12c\ud12d\ud12e\ud12f\ud130\ud131\ud132\ud133\ud134\ud135\ud136\ud137\ud138\ud139\ud13a\ud13b\ud13c\ud13d\ud13e\ud13f\ud140\ud141\ud142\ud143\ud144\ud145\ud146\ud147\ud148\ud149\ud14a\ud14b\ud14c\ud14d\ud14e\ud14f\ud150\ud151\ud152\ud153\ud154\ud155\ud156\ud157\ud158\ud159\ud15a\ud15b\ud15c\ud15d\ud15e\ud15f\ud160\ud161\ud162\ud163\ud164\ud165\ud166\ud167\ud168\ud169\ud16a\ud16b\ud16c\ud16d\ud16e\ud16f\ud170\ud171\ud172\ud173\ud174\ud175\ud176\ud177\ud178\ud179\ud17a\ud17b\ud17c\ud17d\ud17e\ud17f\ud180\ud181\ud182\ud183\ud184\ud185\ud186\ud187\ud188\ud189\ud18a\ud18b\ud18c\ud18d\ud18e\ud18f\ud190\ud191\ud192\ud193\ud194\ud195\ud196\ud197\ud198\ud199\ud19a\ud19b\ud19c\ud19d\ud19e\ud19f\ud1a0\ud1a1\ud1a2\ud1a3\ud1a4\ud1a5\ud1a6\ud1a7\ud1a8\ud1a9\ud1aa\ud1ab\ud1ac\ud1ad\ud1ae\ud1af\ud1b0\ud1b1\ud1b2\ud1b3\ud1b4\ud1b5\ud1b6\ud1b7\ud1b8\ud1b9\ud1ba\ud1bb\ud1bc\ud1bd\ud1be\ud1bf\ud1c0\ud1c1\ud1c2\ud1c3\ud1c4\ud1c5\ud1c6\ud1c7\ud1c8\ud1c9\ud1ca\ud1cb\ud1cc\ud1cd\ud1ce\ud1cf\ud1d0\ud1d1\ud1d2\ud1d3\ud1d4\ud1d5\ud1d6\ud1d7\ud1d8\ud1d9\ud1da\ud1db\ud1dc\ud1dd\ud1de\ud1df\ud1e0\ud1e1\ud1e2\ud1e3\ud1e4\ud1e5\ud1e6\ud1e7\ud1e8\ud1e9\ud1ea\ud1eb\ud1ec\ud1ed\ud1ee\ud1ef\ud1f0\ud1f1\ud1f2\ud1f3\ud1f4\ud1f5\ud1f6\ud1f7\ud1f8\ud1f9\ud1fa\ud1fb\ud1fc\ud1fd\ud1fe\ud1ff\ud200\ud201\ud202\ud203\ud204\ud205\ud206\ud207\ud208\ud209\ud20a\ud20b\ud20c\ud20d\ud20e\ud20f\ud210\ud211\ud212\ud213\ud214\ud215\ud216\ud217\ud218\ud219\ud21a\ud21b\ud21c\ud21d\ud21e\ud21f\ud220\ud221\ud222\ud223\ud224\ud225\ud226\ud227\ud228\ud229\ud22a\ud22b\ud22c\ud22d\ud22e\ud22f\ud230\ud231\ud232\ud233\ud234\ud235\ud236\ud237\ud238\ud239\ud23a\ud23b\ud23c\ud23d\ud23e\ud23f\ud240\ud241\ud242\ud243\ud244\ud245\ud246\ud247\ud248\ud249\ud24a\ud24b\ud24c\ud24d\ud24e\ud24f\ud250\ud251\ud252\ud253\ud254\ud255\ud256\ud257\ud258\ud259\ud25a\ud25b\ud25c\ud25d\ud25e\ud25f\ud260\ud261\ud262\ud263\ud264\ud265\ud266\ud267\ud268\ud269\ud26a\ud26b\ud26c\ud26d\ud26e\ud26f\ud270\ud271\ud272\ud273\ud274\ud275\ud276\ud277\ud278\ud279\ud27a\ud27b\ud27c\ud27d\ud27e\ud27f\ud280\ud281\ud282\ud283\ud284\ud285\ud286\ud287\ud288\ud289\ud28a\ud28b\ud28c\ud28d\ud28e\ud28f\ud290\ud291\ud292\ud293\ud294\ud295\ud296\ud297\ud298\ud299\ud29a\ud29b\ud29c\ud29d\ud29e\ud29f\ud2a0\ud2a1\ud2a2\ud2a3\ud2a4\ud2a5\ud2a6\ud2a7\ud2a8\ud2a9\ud2aa\ud2ab\ud2ac\ud2ad\ud2ae\ud2af\ud2b0\ud2b1\ud2b2\ud2b3\ud2b4\ud2b5\ud2b6\ud2b7\ud2b8\ud2b9\ud2ba\ud2bb\ud2bc\ud2bd\ud2be\ud2bf\ud2c0\ud2c1\ud2c2\ud2c3\ud2c4\ud2c5\ud2c6\ud2c7\ud2c8\ud2c9\ud2ca\ud2cb\ud2cc\ud2cd\ud2ce\ud2cf\ud2d0\ud2d1\ud2d2\ud2d3\ud2d4\ud2d5\ud2d6\ud2d7\ud2d8\ud2d9\ud2da\ud2db\ud2dc\ud2dd\ud2de\ud2df\ud2e0\ud2e1\ud2e2\ud2e3\ud2e4\ud2e5\ud2e6\ud2e7\ud2e8\ud2e9\ud2ea\ud2eb\ud2ec\ud2ed\ud2ee\ud2ef\ud2f0\ud2f1\ud2f2\ud2f3\ud2f4\ud2f5\ud2f6\ud2f7\ud2f8\ud2f9\ud2fa\ud2fb\ud2fc\ud2fd\ud2fe\ud2ff\ud300\ud301\ud302\ud303\ud304\ud305\ud306\ud307\ud308\ud309\ud30a\ud30b\ud30c\ud30d\ud30e\ud30f\ud310\ud311\ud312\ud313\ud314\ud315\ud316\ud317\ud318\ud319\ud31a\ud31b\ud31c\ud31d\ud31e\ud31f\ud320\ud321\ud322\ud323\ud324\ud325\ud326\ud327\ud328\ud329\ud32a\ud32b\ud32c\ud32d\ud32e\ud32f\ud330\ud331\ud332\ud333\ud334\ud335\ud336\ud337\ud338\ud339\ud33a\ud33b\ud33c\ud33d\ud33e\ud33f\ud340\ud341\ud342\ud343\ud344\ud345\ud346\ud347\ud348\ud349\ud34a\ud34b\ud34c\ud34d\ud34e\ud34f\ud350\ud351\ud352\ud353\ud354\ud355\ud356\ud357\ud358\ud359\ud35a\ud35b\ud35c\ud35d\ud35e\ud35f\ud360\ud361\ud362\ud363\ud364\ud365\ud366\ud367\ud368\ud369\ud36a\ud36b\ud36c\ud36d\ud36e\ud36f\ud370\ud371\ud372\ud373\ud374\ud375\ud376\ud377\ud378\ud379\ud37a\ud37b\ud37c\ud37d\ud37e\ud37f\ud380\ud381\ud382\ud383\ud384\ud385\ud386\ud387\ud388\ud389\ud38a\ud38b\ud38c\ud38d\ud38e\ud38f\ud390\ud391\ud392\ud393\ud394\ud395\ud396\ud397\ud398\ud399\ud39a\ud39b\ud39c\ud39d\ud39e\ud39f\ud3a0\ud3a1\ud3a2\ud3a3\ud3a4\ud3a5\ud3a6\ud3a7\ud3a8\ud3a9\ud3aa\ud3ab\ud3ac\ud3ad\ud3ae\ud3af\ud3b0\ud3b1\ud3b2\ud3b3\ud3b4\ud3b5\ud3b6\ud3b7\ud3b8\ud3b9\ud3ba\ud3bb\ud3bc\ud3bd\ud3be\ud3bf\ud3c0\ud3c1\ud3c2\ud3c3\ud3c4\ud3c5\ud3c6\ud3c7\ud3c8\ud3c9\ud3ca\ud3cb\ud3cc\ud3cd\ud3ce\ud3cf\ud3d0\ud3d1\ud3d2\ud3d3\ud3d4\ud3d5\ud3d6\ud3d7\ud3d8\ud3d9\ud3da\ud3db\ud3dc\ud3dd\ud3de\ud3df\ud3e0\ud3e1\ud3e2\ud3e3\ud3e4\ud3e5\ud3e6\ud3e7\ud3e8\ud3e9\ud3ea\ud3eb\ud3ec\ud3ed\ud3ee\ud3ef\ud3f0\ud3f1\ud3f2\ud3f3\ud3f4\ud3f5\ud3f6\ud3f7\ud3f8\ud3f9\ud3fa\ud3fb\ud3fc\ud3fd\ud3fe\ud3ff\ud400\ud401\ud402\ud403\ud404\ud405\ud406\ud407\ud408\ud409\ud40a\ud40b\ud40c\ud40d\ud40e\ud40f\ud410\ud411\ud412\ud413\ud414\ud415\ud416\ud417\ud418\ud419\ud41a\ud41b\ud41c\ud41d\ud41e\ud41f\ud420\ud421\ud422\ud423\ud424\ud425\ud426\ud427\ud428\ud429\ud42a\ud42b\ud42c\ud42d\ud42e\ud42f\ud430\ud431\ud432\ud433\ud434\ud435\ud436\ud437\ud438\ud439\ud43a\ud43b\ud43c\ud43d\ud43e\ud43f\ud440\ud441\ud442\ud443\ud444\ud445\ud446\ud447\ud448\ud449\ud44a\ud44b\ud44c\ud44d\ud44e\ud44f\ud450\ud451\ud452\ud453\ud454\ud455\ud456\ud457\ud458\ud459\ud45a\ud45b\ud45c\ud45d\ud45e\ud45f\ud460\ud461\ud462\ud463\ud464\ud465\ud466\ud467\ud468\ud469\ud46a\ud46b\ud46c\ud46d\ud46e\ud46f\ud470\ud471\ud472\ud473\ud474\ud475\ud476\ud477\ud478\ud479\ud47a\ud47b\ud47c\ud47d\ud47e\ud47f\ud480\ud481\ud482\ud483\ud484\ud485\ud486\ud487\ud488\ud489\ud48a\ud48b\ud48c\ud48d\ud48e\ud48f\ud490\ud491\ud492\ud493\ud494\ud495\ud496\ud497\ud498\ud499\ud49a\ud49b\ud49c\ud49d\ud49e\ud49f\ud4a0\ud4a1\ud4a2\ud4a3\ud4a4\ud4a5\ud4a6\ud4a7\ud4a8\ud4a9\ud4aa\ud4ab\ud4ac\ud4ad\ud4ae\ud4af\ud4b0\ud4b1\ud4b2\ud4b3\ud4b4\ud4b5\ud4b6\ud4b7\ud4b8\ud4b9\ud4ba\ud4bb\ud4bc\ud4bd\ud4be\ud4bf\ud4c0\ud4c1\ud4c2\ud4c3\ud4c4\ud4c5\ud4c6\ud4c7\ud4c8\ud4c9\ud4ca\ud4cb\ud4cc\ud4cd\ud4ce\ud4cf\ud4d0\ud4d1\ud4d2\ud4d3\ud4d4\ud4d5\ud4d6\ud4d7\ud4d8\ud4d9\ud4da\ud4db\ud4dc\ud4dd\ud4de\ud4df\ud4e0\ud4e1\ud4e2\ud4e3\ud4e4\ud4e5\ud4e6\ud4e7\ud4e8\ud4e9\ud4ea\ud4eb\ud4ec\ud4ed\ud4ee\ud4ef\ud4f0\ud4f1\ud4f2\ud4f3\ud4f4\ud4f5\ud4f6\ud4f7\ud4f8\ud4f9\ud4fa\ud4fb\ud4fc\ud4fd\ud4fe\ud4ff\ud500\ud501\ud502\ud503\ud504\ud505\ud506\ud507\ud508\ud509\ud50a\ud50b\ud50c\ud50d\ud50e\ud50f\ud510\ud511\ud512\ud513\ud514\ud515\ud516\ud517\ud518\ud519\ud51a\ud51b\ud51c\ud51d\ud51e\ud51f\ud520\ud521\ud522\ud523\ud524\ud525\ud526\ud527\ud528\ud529\ud52a\ud52b\ud52c\ud52d\ud52e\ud52f\ud530\ud531\ud532\ud533\ud534\ud535\ud536\ud537\ud538\ud539\ud53a\ud53b\ud53c\ud53d\ud53e\ud53f\ud540\ud541\ud542\ud543\ud544\ud545\ud546\ud547\ud548\ud549\ud54a\ud54b\ud54c\ud54d\ud54e\ud54f\ud550\ud551\ud552\ud553\ud554\ud555\ud556\ud557\ud558\ud559\ud55a\ud55b\ud55c\ud55d\ud55e\ud55f\ud560\ud561\ud562\ud563\ud564\ud565\ud566\ud567\ud568\ud569\ud56a\ud56b\ud56c\ud56d\ud56e\ud56f\ud570\ud571\ud572\ud573\ud574\ud575\ud576\ud577\ud578\ud579\ud57a\ud57b\ud57c\ud57d\ud57e\ud57f\ud580\ud581\ud582\ud583\ud584\ud585\ud586\ud587\ud588\ud589\ud58a\ud58b\ud58c\ud58d\ud58e\ud58f\ud590\ud591\ud592\ud593\ud594\ud595\ud596\ud597\ud598\ud599\ud59a\ud59b\ud59c\ud59d\ud59e\ud59f\ud5a0\ud5a1\ud5a2\ud5a3\ud5a4\ud5a5\ud5a6\ud5a7\ud5a8\ud5a9\ud5aa\ud5ab\ud5ac\ud5ad\ud5ae\ud5af\ud5b0\ud5b1\ud5b2\ud5b3\ud5b4\ud5b5\ud5b6\ud5b7\ud5b8\ud5b9\ud5ba\ud5bb\ud5bc\ud5bd\ud5be\ud5bf\ud5c0\ud5c1\ud5c2\ud5c3\ud5c4\ud5c5\ud5c6\ud5c7\ud5c8\ud5c9\ud5ca\ud5cb\ud5cc\ud5cd\ud5ce\ud5cf\ud5d0\ud5d1\ud5d2\ud5d3\ud5d4\ud5d5\ud5d6\ud5d7\ud5d8\ud5d9\ud5da\ud5db\ud5dc\ud5dd\ud5de\ud5df\ud5e0\ud5e1\ud5e2\ud5e3\ud5e4\ud5e5\ud5e6\ud5e7\ud5e8\ud5e9\ud5ea\ud5eb\ud5ec\ud5ed\ud5ee\ud5ef\ud5f0\ud5f1\ud5f2\ud5f3\ud5f4\ud5f5\ud5f6\ud5f7\ud5f8\ud5f9\ud5fa\ud5fb\ud5fc\ud5fd\ud5fe\ud5ff\ud600\ud601\ud602\ud603\ud604\ud605\ud606\ud607\ud608\ud609\ud60a\ud60b\ud60c\ud60d\ud60e\ud60f\ud610\ud611\ud612\ud613\ud614\ud615\ud616\ud617\ud618\ud619\ud61a\ud61b\ud61c\ud61d\ud61e\ud61f\ud620\ud621\ud622\ud623\ud624\ud625\ud626\ud627\ud628\ud629\ud62a\ud62b\ud62c\ud62d\ud62e\ud62f\ud630\ud631\ud632\ud633\ud634\ud635\ud636\ud637\ud638\ud639\ud63a\ud63b\ud63c\ud63d\ud63e\ud63f\ud640\ud641\ud642\ud643\ud644\ud645\ud646\ud647\ud648\ud649\ud64a\ud64b\ud64c\ud64d\ud64e\ud64f\ud650\ud651\ud652\ud653\ud654\ud655\ud656\ud657\ud658\ud659\ud65a\ud65b\ud65c\ud65d\ud65e\ud65f\ud660\ud661\ud662\ud663\ud664\ud665\ud666\ud667\ud668\ud669\ud66a\ud66b\ud66c\ud66d\ud66e\ud66f\ud670\ud671\ud672\ud673\ud674\ud675\ud676\ud677\ud678\ud679\ud67a\ud67b\ud67c\ud67d\ud67e\ud67f\ud680\ud681\ud682\ud683\ud684\ud685\ud686\ud687\ud688\ud689\ud68a\ud68b\ud68c\ud68d\ud68e\ud68f\ud690\ud691\ud692\ud693\ud694\ud695\ud696\ud697\ud698\ud699\ud69a\ud69b\ud69c\ud69d\ud69e\ud69f\ud6a0\ud6a1\ud6a2\ud6a3\ud6a4\ud6a5\ud6a6\ud6a7\ud6a8\ud6a9\ud6aa\ud6ab\ud6ac\ud6ad\ud6ae\ud6af\ud6b0\ud6b1\ud6b2\ud6b3\ud6b4\ud6b5\ud6b6\ud6b7\ud6b8\ud6b9\ud6ba\ud6bb\ud6bc\ud6bd\ud6be\ud6bf\ud6c0\ud6c1\ud6c2\ud6c3\ud6c4\ud6c5\ud6c6\ud6c7\ud6c8\ud6c9\ud6ca\ud6cb\ud6cc\ud6cd\ud6ce\ud6cf\ud6d0\ud6d1\ud6d2\ud6d3\ud6d4\ud6d5\ud6d6\ud6d7\ud6d8\ud6d9\ud6da\ud6db\ud6dc\ud6dd\ud6de\ud6df\ud6e0\ud6e1\ud6e2\ud6e3\ud6e4\ud6e5\ud6e6\ud6e7\ud6e8\ud6e9\ud6ea\ud6eb\ud6ec\ud6ed\ud6ee\ud6ef\ud6f0\ud6f1\ud6f2\ud6f3\ud6f4\ud6f5\ud6f6\ud6f7\ud6f8\ud6f9\ud6fa\ud6fb\ud6fc\ud6fd\ud6fe\ud6ff\ud700\ud701\ud702\ud703\ud704\ud705\ud706\ud707\ud708\ud709\ud70a\ud70b\ud70c\ud70d\ud70e\ud70f\ud710\ud711\ud712\ud713\ud714\ud715\ud716\ud717\ud718\ud719\ud71a\ud71b\ud71c\ud71d\ud71e\ud71f\ud720\ud721\ud722\ud723\ud724\ud725\ud726\ud727\ud728\ud729\ud72a\ud72b\ud72c\ud72d\ud72e\ud72f\ud730\ud731\ud732\ud733\ud734\ud735\ud736\ud737\ud738\ud739\ud73a\ud73b\ud73c\ud73d\ud73e\ud73f\ud740\ud741\ud742\ud743\ud744\ud745\ud746\ud747\ud748\ud749\ud74a\ud74b\ud74c\ud74d\ud74e\ud74f\ud750\ud751\ud752\ud753\ud754\ud755\ud756\ud757\ud758\ud759\ud75a\ud75b\ud75c\ud75d\ud75e\ud75f\ud760\ud761\ud762\ud763\ud764\ud765\ud766\ud767\ud768\ud769\ud76a\ud76b\ud76c\ud76d\ud76e\ud76f\ud770\ud771\ud772\ud773\ud774\ud775\ud776\ud777\ud778\ud779\ud77a\ud77b\ud77c\ud77d\ud77e\ud77f\ud780\ud781\ud782\ud783\ud784\ud785\ud786\ud787\ud788\ud789\ud78a\ud78b\ud78c\ud78d\ud78e\ud78f\ud790\ud791\ud792\ud793\ud794\ud795\ud796\ud797\ud798\ud799\ud79a\ud79b\ud79c\ud79d\ud79e\ud79f\ud7a0\ud7a1\ud7a2\ud7a3\uf900\uf901\uf902\uf903\uf904\uf905\uf906\uf907\uf908\uf909\uf90a\uf90b\uf90c\uf90d\uf90e\uf90f\uf910\uf911\uf912\uf913\uf914\uf915\uf916\uf917\uf918\uf919\uf91a\uf91b\uf91c\uf91d\uf91e\uf91f\uf920\uf921\uf922\uf923\uf924\uf925\uf926\uf927\uf928\uf929\uf92a\uf92b\uf92c\uf92d\uf92e\uf92f\uf930\uf931\uf932\uf933\uf934\uf935\uf936\uf937\uf938\uf939\uf93a\uf93b\uf93c\uf93d\uf93e\uf93f\uf940\uf941\uf942\uf943\uf944\uf945\uf946\uf947\uf948\uf949\uf94a\uf94b\uf94c\uf94d\uf94e\uf94f\uf950\uf951\uf952\uf953\uf954\uf955\uf956\uf957\uf958\uf959\uf95a\uf95b\uf95c\uf95d\uf95e\uf95f\uf960\uf961\uf962\uf963\uf964\uf965\uf966\uf967\uf968\uf969\uf96a\uf96b\uf96c\uf96d\uf96e\uf96f\uf970\uf971\uf972\uf973\uf974\uf975\uf976\uf977\uf978\uf979\uf97a\uf97b\uf97c\uf97d\uf97e\uf97f\uf980\uf981\uf982\uf983\uf984\uf985\uf986\uf987\uf988\uf989\uf98a\uf98b\uf98c\uf98d\uf98e\uf98f\uf990\uf991\uf992\uf993\uf994\uf995\uf996\uf997\uf998\uf999\uf99a\uf99b\uf99c\uf99d\uf99e\uf99f\uf9a0\uf9a1\uf9a2\uf9a3\uf9a4\uf9a5\uf9a6\uf9a7\uf9a8\uf9a9\uf9aa\uf9ab\uf9ac\uf9ad\uf9ae\uf9af\uf9b0\uf9b1\uf9b2\uf9b3\uf9b4\uf9b5\uf9b6\uf9b7\uf9b8\uf9b9\uf9ba\uf9bb\uf9bc\uf9bd\uf9be\uf9bf\uf9c0\uf9c1\uf9c2\uf9c3\uf9c4\uf9c5\uf9c6\uf9c7\uf9c8\uf9c9\uf9ca\uf9cb\uf9cc\uf9cd\uf9ce\uf9cf\uf9d0\uf9d1\uf9d2\uf9d3\uf9d4\uf9d5\uf9d6\uf9d7\uf9d8\uf9d9\uf9da\uf9db\uf9dc\uf9dd\uf9de\uf9df\uf9e0\uf9e1\uf9e2\uf9e3\uf9e4\uf9e5\uf9e6\uf9e7\uf9e8\uf9e9\uf9ea\uf9eb\uf9ec\uf9ed\uf9ee\uf9ef\uf9f0\uf9f1\uf9f2\uf9f3\uf9f4\uf9f5\uf9f6\uf9f7\uf9f8\uf9f9\uf9fa\uf9fb\uf9fc\uf9fd\uf9fe\uf9ff\ufa00\ufa01\ufa02\ufa03\ufa04\ufa05\ufa06\ufa07\ufa08\ufa09\ufa0a\ufa0b\ufa0c\ufa0d\ufa0e\ufa0f\ufa10\ufa11\ufa12\ufa13\ufa14\ufa15\ufa16\ufa17\ufa18\ufa19\ufa1a\ufa1b\ufa1c\ufa1d\ufa1e\ufa1f\ufa20\ufa21\ufa22\ufa23\ufa24\ufa25\ufa26\ufa27\ufa28\ufa29\ufa2a\ufa2b\ufa2c\ufa2d\ufa30\ufa31\ufa32\ufa33\ufa34\ufa35\ufa36\ufa37\ufa38\ufa39\ufa3a\ufa3b\ufa3c\ufa3d\ufa3e\ufa3f\ufa40\ufa41\ufa42\ufa43\ufa44\ufa45\ufa46\ufa47\ufa48\ufa49\ufa4a\ufa4b\ufa4c\ufa4d\ufa4e\ufa4f\ufa50\ufa51\ufa52\ufa53\ufa54\ufa55\ufa56\ufa57\ufa58\ufa59\ufa5a\ufa5b\ufa5c\ufa5d\ufa5e\ufa5f\ufa60\ufa61\ufa62\ufa63\ufa64\ufa65\ufa66\ufa67\ufa68\ufa69\ufa6a\ufa70\ufa71\ufa72\ufa73\ufa74\ufa75\ufa76\ufa77\ufa78\ufa79\ufa7a\ufa7b\ufa7c\ufa7d\ufa7e\ufa7f\ufa80\ufa81\ufa82\ufa83\ufa84\ufa85\ufa86\ufa87\ufa88\ufa89\ufa8a\ufa8b\ufa8c\ufa8d\ufa8e\ufa8f\ufa90\ufa91\ufa92\ufa93\ufa94\ufa95\ufa96\ufa97\ufa98\ufa99\ufa9a\ufa9b\ufa9c\ufa9d\ufa9e\ufa9f\ufaa0\ufaa1\ufaa2\ufaa3\ufaa4\ufaa5\ufaa6\ufaa7\ufaa8\ufaa9\ufaaa\ufaab\ufaac\ufaad\ufaae\ufaaf\ufab0\ufab1\ufab2\ufab3\ufab4\ufab5\ufab6\ufab7\ufab8\ufab9\ufaba\ufabb\ufabc\ufabd\ufabe\ufabf\ufac0\ufac1\ufac2\ufac3\ufac4\ufac5\ufac6\ufac7\ufac8\ufac9\ufaca\ufacb\ufacc\ufacd\uface\ufacf\ufad0\ufad1\ufad2\ufad3\ufad4\ufad5\ufad6\ufad7\ufad8\ufad9\ufb1d\ufb1f\ufb20\ufb21\ufb22\ufb23\ufb24\ufb25\ufb26\ufb27\ufb28\ufb2a\ufb2b\ufb2c\ufb2d\ufb2e\ufb2f\ufb30\ufb31\ufb32\ufb33\ufb34\ufb35\ufb36\ufb38\ufb39\ufb3a\ufb3b\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46\ufb47\ufb48\ufb49\ufb4a\ufb4b\ufb4c\ufb4d\ufb4e\ufb4f\ufb50\ufb51\ufb52\ufb53\ufb54\ufb55\ufb56\ufb57\ufb58\ufb59\ufb5a\ufb5b\ufb5c\ufb5d\ufb5e\ufb5f\ufb60\ufb61\ufb62\ufb63\ufb64\ufb65\ufb66\ufb67\ufb68\ufb69\ufb6a\ufb6b\ufb6c\ufb6d\ufb6e\ufb6f\ufb70\ufb71\ufb72\ufb73\ufb74\ufb75\ufb76\ufb77\ufb78\ufb79\ufb7a\ufb7b\ufb7c\ufb7d\ufb7e\ufb7f\ufb80\ufb81\ufb82\ufb83\ufb84\ufb85\ufb86\ufb87\ufb88\ufb89\ufb8a\ufb8b\ufb8c\ufb8d\ufb8e\ufb8f\ufb90\ufb91\ufb92\ufb93\ufb94\ufb95\ufb96\ufb97\ufb98\ufb99\ufb9a\ufb9b\ufb9c\ufb9d\ufb9e\ufb9f\ufba0\ufba1\ufba2\ufba3\ufba4\ufba5\ufba6\ufba7\ufba8\ufba9\ufbaa\ufbab\ufbac\ufbad\ufbae\ufbaf\ufbb0\ufbb1\ufbd3\ufbd4\ufbd5\ufbd6\ufbd7\ufbd8\ufbd9\ufbda\ufbdb\ufbdc\ufbdd\ufbde\ufbdf\ufbe0\ufbe1\ufbe2\ufbe3\ufbe4\ufbe5\ufbe6\ufbe7\ufbe8\ufbe9\ufbea\ufbeb\ufbec\ufbed\ufbee\ufbef\ufbf0\ufbf1\ufbf2\ufbf3\ufbf4\ufbf5\ufbf6\ufbf7\ufbf8\ufbf9\ufbfa\ufbfb\ufbfc\ufbfd\ufbfe\ufbff\ufc00\ufc01\ufc02\ufc03\ufc04\ufc05\ufc06\ufc07\ufc08\ufc09\ufc0a\ufc0b\ufc0c\ufc0d\ufc0e\ufc0f\ufc10\ufc11\ufc12\ufc13\ufc14\ufc15\ufc16\ufc17\ufc18\ufc19\ufc1a\ufc1b\ufc1c\ufc1d\ufc1e\ufc1f\ufc20\ufc21\ufc22\ufc23\ufc24\ufc25\ufc26\ufc27\ufc28\ufc29\ufc2a\ufc2b\ufc2c\ufc2d\ufc2e\ufc2f\ufc30\ufc31\ufc32\ufc33\ufc34\ufc35\ufc36\ufc37\ufc38\ufc39\ufc3a\ufc3b\ufc3c\ufc3d\ufc3e\ufc3f\ufc40\ufc41\ufc42\ufc43\ufc44\ufc45\ufc46\ufc47\ufc48\ufc49\ufc4a\ufc4b\ufc4c\ufc4d\ufc4e\ufc4f\ufc50\ufc51\ufc52\ufc53\ufc54\ufc55\ufc56\ufc57\ufc58\ufc59\ufc5a\ufc5b\ufc5c\ufc5d\ufc5e\ufc5f\ufc60\ufc61\ufc62\ufc63\ufc64\ufc65\ufc66\ufc67\ufc68\ufc69\ufc6a\ufc6b\ufc6c\ufc6d\ufc6e\ufc6f\ufc70\ufc71\ufc72\ufc73\ufc74\ufc75\ufc76\ufc77\ufc78\ufc79\ufc7a\ufc7b\ufc7c\ufc7d\ufc7e\ufc7f\ufc80\ufc81\ufc82\ufc83\ufc84\ufc85\ufc86\ufc87\ufc88\ufc89\ufc8a\ufc8b\ufc8c\ufc8d\ufc8e\ufc8f\ufc90\ufc91\ufc92\ufc93\ufc94\ufc95\ufc96\ufc97\ufc98\ufc99\ufc9a\ufc9b\ufc9c\ufc9d\ufc9e\ufc9f\ufca0\ufca1\ufca2\ufca3\ufca4\ufca5\ufca6\ufca7\ufca8\ufca9\ufcaa\ufcab\ufcac\ufcad\ufcae\ufcaf\ufcb0\ufcb1\ufcb2\ufcb3\ufcb4\ufcb5\ufcb6\ufcb7\ufcb8\ufcb9\ufcba\ufcbb\ufcbc\ufcbd\ufcbe\ufcbf\ufcc0\ufcc1\ufcc2\ufcc3\ufcc4\ufcc5\ufcc6\ufcc7\ufcc8\ufcc9\ufcca\ufccb\ufccc\ufccd\ufcce\ufccf\ufcd0\ufcd1\ufcd2\ufcd3\ufcd4\ufcd5\ufcd6\ufcd7\ufcd8\ufcd9\ufcda\ufcdb\ufcdc\ufcdd\ufcde\ufcdf\ufce0\ufce1\ufce2\ufce3\ufce4\ufce5\ufce6\ufce7\ufce8\ufce9\ufcea\ufceb\ufcec\ufced\ufcee\ufcef\ufcf0\ufcf1\ufcf2\ufcf3\ufcf4\ufcf5\ufcf6\ufcf7\ufcf8\ufcf9\ufcfa\ufcfb\ufcfc\ufcfd\ufcfe\ufcff\ufd00\ufd01\ufd02\ufd03\ufd04\ufd05\ufd06\ufd07\ufd08\ufd09\ufd0a\ufd0b\ufd0c\ufd0d\ufd0e\ufd0f\ufd10\ufd11\ufd12\ufd13\ufd14\ufd15\ufd16\ufd17\ufd18\ufd19\ufd1a\ufd1b\ufd1c\ufd1d\ufd1e\ufd1f\ufd20\ufd21\ufd22\ufd23\ufd24\ufd25\ufd26\ufd27\ufd28\ufd29\ufd2a\ufd2b\ufd2c\ufd2d\ufd2e\ufd2f\ufd30\ufd31\ufd32\ufd33\ufd34\ufd35\ufd36\ufd37\ufd38\ufd39\ufd3a\ufd3b\ufd3c\ufd3d\ufd50\ufd51\ufd52\ufd53\ufd54\ufd55\ufd56\ufd57\ufd58\ufd59\ufd5a\ufd5b\ufd5c\ufd5d\ufd5e\ufd5f\ufd60\ufd61\ufd62\ufd63\ufd64\ufd65\ufd66\ufd67\ufd68\ufd69\ufd6a\ufd6b\ufd6c\ufd6d\ufd6e\ufd6f\ufd70\ufd71\ufd72\ufd73\ufd74\ufd75\ufd76\ufd77\ufd78\ufd79\ufd7a\ufd7b\ufd7c\ufd7d\ufd7e\ufd7f\ufd80\ufd81\ufd82\ufd83\ufd84\ufd85\ufd86\ufd87\ufd88\ufd89\ufd8a\ufd8b\ufd8c\ufd8d\ufd8e\ufd8f\ufd92\ufd93\ufd94\ufd95\ufd96\ufd97\ufd98\ufd99\ufd9a\ufd9b\ufd9c\ufd9d\ufd9e\ufd9f\ufda0\ufda1\ufda2\ufda3\ufda4\ufda5\ufda6\ufda7\ufda8\ufda9\ufdaa\ufdab\ufdac\ufdad\ufdae\ufdaf\ufdb0\ufdb1\ufdb2\ufdb3\ufdb4\ufdb5\ufdb6\ufdb7\ufdb8\ufdb9\ufdba\ufdbb\ufdbc\ufdbd\ufdbe\ufdbf\ufdc0\ufdc1\ufdc2\ufdc3\ufdc4\ufdc5\ufdc6\ufdc7\ufdf0\ufdf1\ufdf2\ufdf3\ufdf4\ufdf5\ufdf6\ufdf7\ufdf8\ufdf9\ufdfa\ufdfb\ufe70\ufe71\ufe72\ufe73\ufe74\ufe76\ufe77\ufe78\ufe79\ufe7a\ufe7b\ufe7c\ufe7d\ufe7e\ufe7f\ufe80\ufe81\ufe82\ufe83\ufe84\ufe85\ufe86\ufe87\ufe88\ufe89\ufe8a\ufe8b\ufe8c\ufe8d\ufe8e\ufe8f\ufe90\ufe91\ufe92\ufe93\ufe94\ufe95\ufe96\ufe97\ufe98\ufe99\ufe9a\ufe9b\ufe9c\ufe9d\ufe9e\ufe9f\ufea0\ufea1\ufea2\ufea3\ufea4\ufea5\ufea6\ufea7\ufea8\ufea9\ufeaa\ufeab\ufeac\ufead\ufeae\ufeaf\ufeb0\ufeb1\ufeb2\ufeb3\ufeb4\ufeb5\ufeb6\ufeb7\ufeb8\ufeb9\ufeba\ufebb\ufebc\ufebd\ufebe\ufebf\ufec0\ufec1\ufec2\ufec3\ufec4\ufec5\ufec6\ufec7\ufec8\ufec9\ufeca\ufecb\ufecc\ufecd\ufece\ufecf\ufed0\ufed1\ufed2\ufed3\ufed4\ufed5\ufed6\ufed7\ufed8\ufed9\ufeda\ufedb\ufedc\ufedd\ufede\ufedf\ufee0\ufee1\ufee2\ufee3\ufee4\ufee5\ufee6\ufee7\ufee8\ufee9\ufeea\ufeeb\ufeec\ufeed\ufeee\ufeef\ufef0\ufef1\ufef2\ufef3\ufef4\ufef5\ufef6\ufef7\ufef8\ufef9\ufefa\ufefb\ufefc\uff66\uff67\uff68\uff69\uff6a\uff6b\uff6c\uff6d\uff6e\uff6f\uff71\uff72\uff73\uff74\uff75\uff76\uff77\uff78\uff79\uff7a\uff7b\uff7c\uff7d\uff7e\uff7f\uff80\uff81\uff82\uff83\uff84\uff85\uff86\uff87\uff88\uff89\uff8a\uff8b\uff8c\uff8d\uff8e\uff8f\uff90\uff91\uff92\uff93\uff94\uff95\uff96\uff97\uff98\uff99\uff9a\uff9b\uff9c\uff9d\uffa0\uffa1\uffa2\uffa3\uffa4\uffa5\uffa6\uffa7\uffa8\uffa9\uffaa\uffab\uffac\uffad\uffae\uffaf\uffb0\uffb1\uffb2\uffb3\uffb4\uffb5\uffb6\uffb7\uffb8\uffb9\uffba\uffbb\uffbc\uffbd\uffbe\uffc2\uffc3\uffc4\uffc5\uffc6\uffc7\uffca\uffcb\uffcc\uffcd\uffce\uffcf\uffd2\uffd3\uffd4\uffd5\uffd6\uffd7\uffda\uffdb\uffdc' + +Lt = u'\u01c5\u01c8\u01cb\u01f2\u1f88\u1f89\u1f8a\u1f8b\u1f8c\u1f8d\u1f8e\u1f8f\u1f98\u1f99\u1f9a\u1f9b\u1f9c\u1f9d\u1f9e\u1f9f\u1fa8\u1fa9\u1faa\u1fab\u1fac\u1fad\u1fae\u1faf\u1fbc\u1fcc\u1ffc' + +Lu = u'ABCDEFGHIJKLMNOPQRSTUVWXYZ\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd8\xd9\xda\xdb\xdc\xdd\xde\u0100\u0102\u0104\u0106\u0108\u010a\u010c\u010e\u0110\u0112\u0114\u0116\u0118\u011a\u011c\u011e\u0120\u0122\u0124\u0126\u0128\u012a\u012c\u012e\u0130\u0132\u0134\u0136\u0139\u013b\u013d\u013f\u0141\u0143\u0145\u0147\u014a\u014c\u014e\u0150\u0152\u0154\u0156\u0158\u015a\u015c\u015e\u0160\u0162\u0164\u0166\u0168\u016a\u016c\u016e\u0170\u0172\u0174\u0176\u0178\u0179\u017b\u017d\u0181\u0182\u0184\u0186\u0187\u0189\u018a\u018b\u018e\u018f\u0190\u0191\u0193\u0194\u0196\u0197\u0198\u019c\u019d\u019f\u01a0\u01a2\u01a4\u01a6\u01a7\u01a9\u01ac\u01ae\u01af\u01b1\u01b2\u01b3\u01b5\u01b7\u01b8\u01bc\u01c4\u01c7\u01ca\u01cd\u01cf\u01d1\u01d3\u01d5\u01d7\u01d9\u01db\u01de\u01e0\u01e2\u01e4\u01e6\u01e8\u01ea\u01ec\u01ee\u01f1\u01f4\u01f6\u01f7\u01f8\u01fa\u01fc\u01fe\u0200\u0202\u0204\u0206\u0208\u020a\u020c\u020e\u0210\u0212\u0214\u0216\u0218\u021a\u021c\u021e\u0220\u0222\u0224\u0226\u0228\u022a\u022c\u022e\u0230\u0232\u023a\u023b\u023d\u023e\u0241\u0386\u0388\u0389\u038a\u038c\u038e\u038f\u0391\u0392\u0393\u0394\u0395\u0396\u0397\u0398\u0399\u039a\u039b\u039c\u039d\u039e\u039f\u03a0\u03a1\u03a3\u03a4\u03a5\u03a6\u03a7\u03a8\u03a9\u03aa\u03ab\u03d2\u03d3\u03d4\u03d8\u03da\u03dc\u03de\u03e0\u03e2\u03e4\u03e6\u03e8\u03ea\u03ec\u03ee\u03f4\u03f7\u03f9\u03fa\u03fd\u03fe\u03ff\u0400\u0401\u0402\u0403\u0404\u0405\u0406\u0407\u0408\u0409\u040a\u040b\u040c\u040d\u040e\u040f\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041a\u041b\u041c\u041d\u041e\u041f\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042a\u042b\u042c\u042d\u042e\u042f\u0460\u0462\u0464\u0466\u0468\u046a\u046c\u046e\u0470\u0472\u0474\u0476\u0478\u047a\u047c\u047e\u0480\u048a\u048c\u048e\u0490\u0492\u0494\u0496\u0498\u049a\u049c\u049e\u04a0\u04a2\u04a4\u04a6\u04a8\u04aa\u04ac\u04ae\u04b0\u04b2\u04b4\u04b6\u04b8\u04ba\u04bc\u04be\u04c0\u04c1\u04c3\u04c5\u04c7\u04c9\u04cb\u04cd\u04d0\u04d2\u04d4\u04d6\u04d8\u04da\u04dc\u04de\u04e0\u04e2\u04e4\u04e6\u04e8\u04ea\u04ec\u04ee\u04f0\u04f2\u04f4\u04f6\u04f8\u0500\u0502\u0504\u0506\u0508\u050a\u050c\u050e\u0531\u0532\u0533\u0534\u0535\u0536\u0537\u0538\u0539\u053a\u053b\u053c\u053d\u053e\u053f\u0540\u0541\u0542\u0543\u0544\u0545\u0546\u0547\u0548\u0549\u054a\u054b\u054c\u054d\u054e\u054f\u0550\u0551\u0552\u0553\u0554\u0555\u0556\u10a0\u10a1\u10a2\u10a3\u10a4\u10a5\u10a6\u10a7\u10a8\u10a9\u10aa\u10ab\u10ac\u10ad\u10ae\u10af\u10b0\u10b1\u10b2\u10b3\u10b4\u10b5\u10b6\u10b7\u10b8\u10b9\u10ba\u10bb\u10bc\u10bd\u10be\u10bf\u10c0\u10c1\u10c2\u10c3\u10c4\u10c5\u1e00\u1e02\u1e04\u1e06\u1e08\u1e0a\u1e0c\u1e0e\u1e10\u1e12\u1e14\u1e16\u1e18\u1e1a\u1e1c\u1e1e\u1e20\u1e22\u1e24\u1e26\u1e28\u1e2a\u1e2c\u1e2e\u1e30\u1e32\u1e34\u1e36\u1e38\u1e3a\u1e3c\u1e3e\u1e40\u1e42\u1e44\u1e46\u1e48\u1e4a\u1e4c\u1e4e\u1e50\u1e52\u1e54\u1e56\u1e58\u1e5a\u1e5c\u1e5e\u1e60\u1e62\u1e64\u1e66\u1e68\u1e6a\u1e6c\u1e6e\u1e70\u1e72\u1e74\u1e76\u1e78\u1e7a\u1e7c\u1e7e\u1e80\u1e82\u1e84\u1e86\u1e88\u1e8a\u1e8c\u1e8e\u1e90\u1e92\u1e94\u1ea0\u1ea2\u1ea4\u1ea6\u1ea8\u1eaa\u1eac\u1eae\u1eb0\u1eb2\u1eb4\u1eb6\u1eb8\u1eba\u1ebc\u1ebe\u1ec0\u1ec2\u1ec4\u1ec6\u1ec8\u1eca\u1ecc\u1ece\u1ed0\u1ed2\u1ed4\u1ed6\u1ed8\u1eda\u1edc\u1ede\u1ee0\u1ee2\u1ee4\u1ee6\u1ee8\u1eea\u1eec\u1eee\u1ef0\u1ef2\u1ef4\u1ef6\u1ef8\u1f08\u1f09\u1f0a\u1f0b\u1f0c\u1f0d\u1f0e\u1f0f\u1f18\u1f19\u1f1a\u1f1b\u1f1c\u1f1d\u1f28\u1f29\u1f2a\u1f2b\u1f2c\u1f2d\u1f2e\u1f2f\u1f38\u1f39\u1f3a\u1f3b\u1f3c\u1f3d\u1f3e\u1f3f\u1f48\u1f49\u1f4a\u1f4b\u1f4c\u1f4d\u1f59\u1f5b\u1f5d\u1f5f\u1f68\u1f69\u1f6a\u1f6b\u1f6c\u1f6d\u1f6e\u1f6f\u1fb8\u1fb9\u1fba\u1fbb\u1fc8\u1fc9\u1fca\u1fcb\u1fd8\u1fd9\u1fda\u1fdb\u1fe8\u1fe9\u1fea\u1feb\u1fec\u1ff8\u1ff9\u1ffa\u1ffb\u2102\u2107\u210b\u210c\u210d\u2110\u2111\u2112\u2115\u2119\u211a\u211b\u211c\u211d\u2124\u2126\u2128\u212a\u212b\u212c\u212d\u2130\u2131\u2133\u213e\u213f\u2145\u2c00\u2c01\u2c02\u2c03\u2c04\u2c05\u2c06\u2c07\u2c08\u2c09\u2c0a\u2c0b\u2c0c\u2c0d\u2c0e\u2c0f\u2c10\u2c11\u2c12\u2c13\u2c14\u2c15\u2c16\u2c17\u2c18\u2c19\u2c1a\u2c1b\u2c1c\u2c1d\u2c1e\u2c1f\u2c20\u2c21\u2c22\u2c23\u2c24\u2c25\u2c26\u2c27\u2c28\u2c29\u2c2a\u2c2b\u2c2c\u2c2d\u2c2e\u2c80\u2c82\u2c84\u2c86\u2c88\u2c8a\u2c8c\u2c8e\u2c90\u2c92\u2c94\u2c96\u2c98\u2c9a\u2c9c\u2c9e\u2ca0\u2ca2\u2ca4\u2ca6\u2ca8\u2caa\u2cac\u2cae\u2cb0\u2cb2\u2cb4\u2cb6\u2cb8\u2cba\u2cbc\u2cbe\u2cc0\u2cc2\u2cc4\u2cc6\u2cc8\u2cca\u2ccc\u2cce\u2cd0\u2cd2\u2cd4\u2cd6\u2cd8\u2cda\u2cdc\u2cde\u2ce0\u2ce2\uff21\uff22\uff23\uff24\uff25\uff26\uff27\uff28\uff29\uff2a\uff2b\uff2c\uff2d\uff2e\uff2f\uff30\uff31\uff32\uff33\uff34\uff35\uff36\uff37\uff38\uff39\uff3a' + +Mc = u'\u0903\u093e\u093f\u0940\u0949\u094a\u094b\u094c\u0982\u0983\u09be\u09bf\u09c0\u09c7\u09c8\u09cb\u09cc\u09d7\u0a03\u0a3e\u0a3f\u0a40\u0a83\u0abe\u0abf\u0ac0\u0ac9\u0acb\u0acc\u0b02\u0b03\u0b3e\u0b40\u0b47\u0b48\u0b4b\u0b4c\u0b57\u0bbe\u0bbf\u0bc1\u0bc2\u0bc6\u0bc7\u0bc8\u0bca\u0bcb\u0bcc\u0bd7\u0c01\u0c02\u0c03\u0c41\u0c42\u0c43\u0c44\u0c82\u0c83\u0cbe\u0cc0\u0cc1\u0cc2\u0cc3\u0cc4\u0cc7\u0cc8\u0cca\u0ccb\u0cd5\u0cd6\u0d02\u0d03\u0d3e\u0d3f\u0d40\u0d46\u0d47\u0d48\u0d4a\u0d4b\u0d4c\u0d57\u0d82\u0d83\u0dcf\u0dd0\u0dd1\u0dd8\u0dd9\u0dda\u0ddb\u0ddc\u0ddd\u0dde\u0ddf\u0df2\u0df3\u0f3e\u0f3f\u0f7f\u102c\u1031\u1038\u1056\u1057\u17b6\u17be\u17bf\u17c0\u17c1\u17c2\u17c3\u17c4\u17c5\u17c7\u17c8\u1923\u1924\u1925\u1926\u1929\u192a\u192b\u1930\u1931\u1933\u1934\u1935\u1936\u1937\u1938\u19b0\u19b1\u19b2\u19b3\u19b4\u19b5\u19b6\u19b7\u19b8\u19b9\u19ba\u19bb\u19bc\u19bd\u19be\u19bf\u19c0\u19c8\u19c9\u1a19\u1a1a\u1a1b\ua802\ua823\ua824\ua827' + +Me = u'\u0488\u0489\u06de\u20dd\u20de\u20df\u20e0\u20e2\u20e3\u20e4' + +Mn = u'\u0300\u0301\u0302\u0303\u0304\u0305\u0306\u0307\u0308\u0309\u030a\u030b\u030c\u030d\u030e\u030f\u0310\u0311\u0312\u0313\u0314\u0315\u0316\u0317\u0318\u0319\u031a\u031b\u031c\u031d\u031e\u031f\u0320\u0321\u0322\u0323\u0324\u0325\u0326\u0327\u0328\u0329\u032a\u032b\u032c\u032d\u032e\u032f\u0330\u0331\u0332\u0333\u0334\u0335\u0336\u0337\u0338\u0339\u033a\u033b\u033c\u033d\u033e\u033f\u0340\u0341\u0342\u0343\u0344\u0345\u0346\u0347\u0348\u0349\u034a\u034b\u034c\u034d\u034e\u034f\u0350\u0351\u0352\u0353\u0354\u0355\u0356\u0357\u0358\u0359\u035a\u035b\u035c\u035d\u035e\u035f\u0360\u0361\u0362\u0363\u0364\u0365\u0366\u0367\u0368\u0369\u036a\u036b\u036c\u036d\u036e\u036f\u0483\u0484\u0485\u0486\u0591\u0592\u0593\u0594\u0595\u0596\u0597\u0598\u0599\u059a\u059b\u059c\u059d\u059e\u059f\u05a0\u05a1\u05a2\u05a3\u05a4\u05a5\u05a6\u05a7\u05a8\u05a9\u05aa\u05ab\u05ac\u05ad\u05ae\u05af\u05b0\u05b1\u05b2\u05b3\u05b4\u05b5\u05b6\u05b7\u05b8\u05b9\u05bb\u05bc\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610\u0611\u0612\u0613\u0614\u0615\u064b\u064c\u064d\u064e\u064f\u0650\u0651\u0652\u0653\u0654\u0655\u0656\u0657\u0658\u0659\u065a\u065b\u065c\u065d\u065e\u0670\u06d6\u06d7\u06d8\u06d9\u06da\u06db\u06dc\u06df\u06e0\u06e1\u06e2\u06e3\u06e4\u06e7\u06e8\u06ea\u06eb\u06ec\u06ed\u0711\u0730\u0731\u0732\u0733\u0734\u0735\u0736\u0737\u0738\u0739\u073a\u073b\u073c\u073d\u073e\u073f\u0740\u0741\u0742\u0743\u0744\u0745\u0746\u0747\u0748\u0749\u074a\u07a6\u07a7\u07a8\u07a9\u07aa\u07ab\u07ac\u07ad\u07ae\u07af\u07b0\u0901\u0902\u093c\u0941\u0942\u0943\u0944\u0945\u0946\u0947\u0948\u094d\u0951\u0952\u0953\u0954\u0962\u0963\u0981\u09bc\u09c1\u09c2\u09c3\u09c4\u09cd\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b\u0a4c\u0a4d\u0a70\u0a71\u0a81\u0a82\u0abc\u0ac1\u0ac2\u0ac3\u0ac4\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3f\u0b41\u0b42\u0b43\u0b4d\u0b56\u0b82\u0bc0\u0bcd\u0c3e\u0c3f\u0c40\u0c46\u0c47\u0c48\u0c4a\u0c4b\u0c4c\u0c4d\u0c55\u0c56\u0cbc\u0cbf\u0cc6\u0ccc\u0ccd\u0d41\u0d42\u0d43\u0d4d\u0dca\u0dd2\u0dd3\u0dd4\u0dd6\u0e31\u0e34\u0e35\u0e36\u0e37\u0e38\u0e39\u0e3a\u0e47\u0e48\u0e49\u0e4a\u0e4b\u0e4c\u0e4d\u0e4e\u0eb1\u0eb4\u0eb5\u0eb6\u0eb7\u0eb8\u0eb9\u0ebb\u0ebc\u0ec8\u0ec9\u0eca\u0ecb\u0ecc\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71\u0f72\u0f73\u0f74\u0f75\u0f76\u0f77\u0f78\u0f79\u0f7a\u0f7b\u0f7c\u0f7d\u0f7e\u0f80\u0f81\u0f82\u0f83\u0f84\u0f86\u0f87\u0f90\u0f91\u0f92\u0f93\u0f94\u0f95\u0f96\u0f97\u0f99\u0f9a\u0f9b\u0f9c\u0f9d\u0f9e\u0f9f\u0fa0\u0fa1\u0fa2\u0fa3\u0fa4\u0fa5\u0fa6\u0fa7\u0fa8\u0fa9\u0faa\u0fab\u0fac\u0fad\u0fae\u0faf\u0fb0\u0fb1\u0fb2\u0fb3\u0fb4\u0fb5\u0fb6\u0fb7\u0fb8\u0fb9\u0fba\u0fbb\u0fbc\u0fc6\u102d\u102e\u102f\u1030\u1032\u1036\u1037\u1039\u1058\u1059\u135f\u1712\u1713\u1714\u1732\u1733\u1734\u1752\u1753\u1772\u1773\u17b7\u17b8\u17b9\u17ba\u17bb\u17bc\u17bd\u17c6\u17c9\u17ca\u17cb\u17cc\u17cd\u17ce\u17cf\u17d0\u17d1\u17d2\u17d3\u17dd\u180b\u180c\u180d\u18a9\u1920\u1921\u1922\u1927\u1928\u1932\u1939\u193a\u193b\u1a17\u1a18\u1dc0\u1dc1\u1dc2\u1dc3\u20d0\u20d1\u20d2\u20d3\u20d4\u20d5\u20d6\u20d7\u20d8\u20d9\u20da\u20db\u20dc\u20e1\u20e5\u20e6\u20e7\u20e8\u20e9\u20ea\u20eb\u302a\u302b\u302c\u302d\u302e\u302f\u3099\u309a\ua806\ua80b\ua825\ua826\ufb1e\ufe00\ufe01\ufe02\ufe03\ufe04\ufe05\ufe06\ufe07\ufe08\ufe09\ufe0a\ufe0b\ufe0c\ufe0d\ufe0e\ufe0f\ufe20\ufe21\ufe22\ufe23' + +Nd = u'0123456789\u0660\u0661\u0662\u0663\u0664\u0665\u0666\u0667\u0668\u0669\u06f0\u06f1\u06f2\u06f3\u06f4\u06f5\u06f6\u06f7\u06f8\u06f9\u0966\u0967\u0968\u0969\u096a\u096b\u096c\u096d\u096e\u096f\u09e6\u09e7\u09e8\u09e9\u09ea\u09eb\u09ec\u09ed\u09ee\u09ef\u0a66\u0a67\u0a68\u0a69\u0a6a\u0a6b\u0a6c\u0a6d\u0a6e\u0a6f\u0ae6\u0ae7\u0ae8\u0ae9\u0aea\u0aeb\u0aec\u0aed\u0aee\u0aef\u0b66\u0b67\u0b68\u0b69\u0b6a\u0b6b\u0b6c\u0b6d\u0b6e\u0b6f\u0be6\u0be7\u0be8\u0be9\u0bea\u0beb\u0bec\u0bed\u0bee\u0bef\u0c66\u0c67\u0c68\u0c69\u0c6a\u0c6b\u0c6c\u0c6d\u0c6e\u0c6f\u0ce6\u0ce7\u0ce8\u0ce9\u0cea\u0ceb\u0cec\u0ced\u0cee\u0cef\u0d66\u0d67\u0d68\u0d69\u0d6a\u0d6b\u0d6c\u0d6d\u0d6e\u0d6f\u0e50\u0e51\u0e52\u0e53\u0e54\u0e55\u0e56\u0e57\u0e58\u0e59\u0ed0\u0ed1\u0ed2\u0ed3\u0ed4\u0ed5\u0ed6\u0ed7\u0ed8\u0ed9\u0f20\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28\u0f29\u1040\u1041\u1042\u1043\u1044\u1045\u1046\u1047\u1048\u1049\u17e0\u17e1\u17e2\u17e3\u17e4\u17e5\u17e6\u17e7\u17e8\u17e9\u1810\u1811\u1812\u1813\u1814\u1815\u1816\u1817\u1818\u1819\u1946\u1947\u1948\u1949\u194a\u194b\u194c\u194d\u194e\u194f\u19d0\u19d1\u19d2\u19d3\u19d4\u19d5\u19d6\u19d7\u19d8\u19d9\uff10\uff11\uff12\uff13\uff14\uff15\uff16\uff17\uff18\uff19' + +Nl = u'\u16ee\u16ef\u16f0\u2160\u2161\u2162\u2163\u2164\u2165\u2166\u2167\u2168\u2169\u216a\u216b\u216c\u216d\u216e\u216f\u2170\u2171\u2172\u2173\u2174\u2175\u2176\u2177\u2178\u2179\u217a\u217b\u217c\u217d\u217e\u217f\u2180\u2181\u2182\u2183\u3007\u3021\u3022\u3023\u3024\u3025\u3026\u3027\u3028\u3029\u3038\u3039\u303a' + +No = u'\xb2\xb3\xb9\xbc\xbd\xbe\u09f4\u09f5\u09f6\u09f7\u09f8\u09f9\u0bf0\u0bf1\u0bf2\u0f2a\u0f2b\u0f2c\u0f2d\u0f2e\u0f2f\u0f30\u0f31\u0f32\u0f33\u1369\u136a\u136b\u136c\u136d\u136e\u136f\u1370\u1371\u1372\u1373\u1374\u1375\u1376\u1377\u1378\u1379\u137a\u137b\u137c\u17f0\u17f1\u17f2\u17f3\u17f4\u17f5\u17f6\u17f7\u17f8\u17f9\u2070\u2074\u2075\u2076\u2077\u2078\u2079\u2080\u2081\u2082\u2083\u2084\u2085\u2086\u2087\u2088\u2089\u2153\u2154\u2155\u2156\u2157\u2158\u2159\u215a\u215b\u215c\u215d\u215e\u215f\u2460\u2461\u2462\u2463\u2464\u2465\u2466\u2467\u2468\u2469\u246a\u246b\u246c\u246d\u246e\u246f\u2470\u2471\u2472\u2473\u2474\u2475\u2476\u2477\u2478\u2479\u247a\u247b\u247c\u247d\u247e\u247f\u2480\u2481\u2482\u2483\u2484\u2485\u2486\u2487\u2488\u2489\u248a\u248b\u248c\u248d\u248e\u248f\u2490\u2491\u2492\u2493\u2494\u2495\u2496\u2497\u2498\u2499\u249a\u249b\u24ea\u24eb\u24ec\u24ed\u24ee\u24ef\u24f0\u24f1\u24f2\u24f3\u24f4\u24f5\u24f6\u24f7\u24f8\u24f9\u24fa\u24fb\u24fc\u24fd\u24fe\u24ff\u2776\u2777\u2778\u2779\u277a\u277b\u277c\u277d\u277e\u277f\u2780\u2781\u2782\u2783\u2784\u2785\u2786\u2787\u2788\u2789\u278a\u278b\u278c\u278d\u278e\u278f\u2790\u2791\u2792\u2793\u2cfd\u3192\u3193\u3194\u3195\u3220\u3221\u3222\u3223\u3224\u3225\u3226\u3227\u3228\u3229\u3251\u3252\u3253\u3254\u3255\u3256\u3257\u3258\u3259\u325a\u325b\u325c\u325d\u325e\u325f\u3280\u3281\u3282\u3283\u3284\u3285\u3286\u3287\u3288\u3289\u32b1\u32b2\u32b3\u32b4\u32b5\u32b6\u32b7\u32b8\u32b9\u32ba\u32bb\u32bc\u32bd\u32be\u32bf' + +Pc = u'_\u203f\u2040\u2054\ufe33\ufe34\ufe4d\ufe4e\ufe4f\uff3f' + +Pd = u'-\u058a\u1806\u2010\u2011\u2012\u2013\u2014\u2015\u2e17\u301c\u3030\u30a0\ufe31\ufe32\ufe58\ufe63\uff0d' + +Pe = u')]}\u0f3b\u0f3d\u169c\u2046\u207e\u208e\u232a\u23b5\u2769\u276b\u276d\u276f\u2771\u2773\u2775\u27c6\u27e7\u27e9\u27eb\u2984\u2986\u2988\u298a\u298c\u298e\u2990\u2992\u2994\u2996\u2998\u29d9\u29db\u29fd\u3009\u300b\u300d\u300f\u3011\u3015\u3017\u3019\u301b\u301e\u301f\ufd3f\ufe18\ufe36\ufe38\ufe3a\ufe3c\ufe3e\ufe40\ufe42\ufe44\ufe48\ufe5a\ufe5c\ufe5e\uff09\uff3d\uff5d\uff60\uff63' + +Pf = u'\xbb\u2019\u201d\u203a\u2e03\u2e05\u2e0a\u2e0d\u2e1d' + +Pi = u'\xab\u2018\u201b\u201c\u201f\u2039\u2e02\u2e04\u2e09\u2e0c\u2e1c' + +Po = u'!"#%&\'*,./:;?@\\\xa1\xb7\xbf\u037e\u0387\u055a\u055b\u055c\u055d\u055e\u055f\u0589\u05be\u05c0\u05c3\u05c6\u05f3\u05f4\u060c\u060d\u061b\u061e\u061f\u066a\u066b\u066c\u066d\u06d4\u0700\u0701\u0702\u0703\u0704\u0705\u0706\u0707\u0708\u0709\u070a\u070b\u070c\u070d\u0964\u0965\u0970\u0df4\u0e4f\u0e5a\u0e5b\u0f04\u0f05\u0f06\u0f07\u0f08\u0f09\u0f0a\u0f0b\u0f0c\u0f0d\u0f0e\u0f0f\u0f10\u0f11\u0f12\u0f85\u0fd0\u0fd1\u104a\u104b\u104c\u104d\u104e\u104f\u10fb\u1361\u1362\u1363\u1364\u1365\u1366\u1367\u1368\u166d\u166e\u16eb\u16ec\u16ed\u1735\u1736\u17d4\u17d5\u17d6\u17d8\u17d9\u17da\u1800\u1801\u1802\u1803\u1804\u1805\u1807\u1808\u1809\u180a\u1944\u1945\u19de\u19df\u1a1e\u1a1f\u2016\u2017\u2020\u2021\u2022\u2023\u2024\u2025\u2026\u2027\u2030\u2031\u2032\u2033\u2034\u2035\u2036\u2037\u2038\u203b\u203c\u203d\u203e\u2041\u2042\u2043\u2047\u2048\u2049\u204a\u204b\u204c\u204d\u204e\u204f\u2050\u2051\u2053\u2055\u2056\u2057\u2058\u2059\u205a\u205b\u205c\u205d\u205e\u23b6\u2cf9\u2cfa\u2cfb\u2cfc\u2cfe\u2cff\u2e00\u2e01\u2e06\u2e07\u2e08\u2e0b\u2e0e\u2e0f\u2e10\u2e11\u2e12\u2e13\u2e14\u2e15\u2e16\u3001\u3002\u3003\u303d\u30fb\ufe10\ufe11\ufe12\ufe13\ufe14\ufe15\ufe16\ufe19\ufe30\ufe45\ufe46\ufe49\ufe4a\ufe4b\ufe4c\ufe50\ufe51\ufe52\ufe54\ufe55\ufe56\ufe57\ufe5f\ufe60\ufe61\ufe68\ufe6a\ufe6b\uff01\uff02\uff03\uff05\uff06\uff07\uff0a\uff0c\uff0e\uff0f\uff1a\uff1b\uff1f\uff20\uff3c\uff61\uff64\uff65' + +Ps = u'([{\u0f3a\u0f3c\u169b\u201a\u201e\u2045\u207d\u208d\u2329\u23b4\u2768\u276a\u276c\u276e\u2770\u2772\u2774\u27c5\u27e6\u27e8\u27ea\u2983\u2985\u2987\u2989\u298b\u298d\u298f\u2991\u2993\u2995\u2997\u29d8\u29da\u29fc\u3008\u300a\u300c\u300e\u3010\u3014\u3016\u3018\u301a\u301d\ufd3e\ufe17\ufe35\ufe37\ufe39\ufe3b\ufe3d\ufe3f\ufe41\ufe43\ufe47\ufe59\ufe5b\ufe5d\uff08\uff3b\uff5b\uff5f\uff62' + +Sc = u'$\xa2\xa3\xa4\xa5\u060b\u09f2\u09f3\u0af1\u0bf9\u0e3f\u17db\u20a0\u20a1\u20a2\u20a3\u20a4\u20a5\u20a6\u20a7\u20a8\u20a9\u20aa\u20ab\u20ac\u20ad\u20ae\u20af\u20b0\u20b1\u20b2\u20b3\u20b4\u20b5\ufdfc\ufe69\uff04\uffe0\uffe1\uffe5\uffe6' + +Sk = u'^`\xa8\xaf\xb4\xb8\u02c2\u02c3\u02c4\u02c5\u02d2\u02d3\u02d4\u02d5\u02d6\u02d7\u02d8\u02d9\u02da\u02db\u02dc\u02dd\u02de\u02df\u02e5\u02e6\u02e7\u02e8\u02e9\u02ea\u02eb\u02ec\u02ed\u02ef\u02f0\u02f1\u02f2\u02f3\u02f4\u02f5\u02f6\u02f7\u02f8\u02f9\u02fa\u02fb\u02fc\u02fd\u02fe\u02ff\u0374\u0375\u0384\u0385\u1fbd\u1fbf\u1fc0\u1fc1\u1fcd\u1fce\u1fcf\u1fdd\u1fde\u1fdf\u1fed\u1fee\u1fef\u1ffd\u1ffe\u309b\u309c\ua700\ua701\ua702\ua703\ua704\ua705\ua706\ua707\ua708\ua709\ua70a\ua70b\ua70c\ua70d\ua70e\ua70f\ua710\ua711\ua712\ua713\ua714\ua715\ua716\uff3e\uff40\uffe3' + +Sm = u'+<=>|~\xac\xb1\xd7\xf7\u03f6\u2044\u2052\u207a\u207b\u207c\u208a\u208b\u208c\u2140\u2141\u2142\u2143\u2144\u214b\u2190\u2191\u2192\u2193\u2194\u219a\u219b\u21a0\u21a3\u21a6\u21ae\u21ce\u21cf\u21d2\u21d4\u21f4\u21f5\u21f6\u21f7\u21f8\u21f9\u21fa\u21fb\u21fc\u21fd\u21fe\u21ff\u2200\u2201\u2202\u2203\u2204\u2205\u2206\u2207\u2208\u2209\u220a\u220b\u220c\u220d\u220e\u220f\u2210\u2211\u2212\u2213\u2214\u2215\u2216\u2217\u2218\u2219\u221a\u221b\u221c\u221d\u221e\u221f\u2220\u2221\u2222\u2223\u2224\u2225\u2226\u2227\u2228\u2229\u222a\u222b\u222c\u222d\u222e\u222f\u2230\u2231\u2232\u2233\u2234\u2235\u2236\u2237\u2238\u2239\u223a\u223b\u223c\u223d\u223e\u223f\u2240\u2241\u2242\u2243\u2244\u2245\u2246\u2247\u2248\u2249\u224a\u224b\u224c\u224d\u224e\u224f\u2250\u2251\u2252\u2253\u2254\u2255\u2256\u2257\u2258\u2259\u225a\u225b\u225c\u225d\u225e\u225f\u2260\u2261\u2262\u2263\u2264\u2265\u2266\u2267\u2268\u2269\u226a\u226b\u226c\u226d\u226e\u226f\u2270\u2271\u2272\u2273\u2274\u2275\u2276\u2277\u2278\u2279\u227a\u227b\u227c\u227d\u227e\u227f\u2280\u2281\u2282\u2283\u2284\u2285\u2286\u2287\u2288\u2289\u228a\u228b\u228c\u228d\u228e\u228f\u2290\u2291\u2292\u2293\u2294\u2295\u2296\u2297\u2298\u2299\u229a\u229b\u229c\u229d\u229e\u229f\u22a0\u22a1\u22a2\u22a3\u22a4\u22a5\u22a6\u22a7\u22a8\u22a9\u22aa\u22ab\u22ac\u22ad\u22ae\u22af\u22b0\u22b1\u22b2\u22b3\u22b4\u22b5\u22b6\u22b7\u22b8\u22b9\u22ba\u22bb\u22bc\u22bd\u22be\u22bf\u22c0\u22c1\u22c2\u22c3\u22c4\u22c5\u22c6\u22c7\u22c8\u22c9\u22ca\u22cb\u22cc\u22cd\u22ce\u22cf\u22d0\u22d1\u22d2\u22d3\u22d4\u22d5\u22d6\u22d7\u22d8\u22d9\u22da\u22db\u22dc\u22dd\u22de\u22df\u22e0\u22e1\u22e2\u22e3\u22e4\u22e5\u22e6\u22e7\u22e8\u22e9\u22ea\u22eb\u22ec\u22ed\u22ee\u22ef\u22f0\u22f1\u22f2\u22f3\u22f4\u22f5\u22f6\u22f7\u22f8\u22f9\u22fa\u22fb\u22fc\u22fd\u22fe\u22ff\u2308\u2309\u230a\u230b\u2320\u2321\u237c\u239b\u239c\u239d\u239e\u239f\u23a0\u23a1\u23a2\u23a3\u23a4\u23a5\u23a6\u23a7\u23a8\u23a9\u23aa\u23ab\u23ac\u23ad\u23ae\u23af\u23b0\u23b1\u23b2\u23b3\u25b7\u25c1\u25f8\u25f9\u25fa\u25fb\u25fc\u25fd\u25fe\u25ff\u266f\u27c0\u27c1\u27c2\u27c3\u27c4\u27d0\u27d1\u27d2\u27d3\u27d4\u27d5\u27d6\u27d7\u27d8\u27d9\u27da\u27db\u27dc\u27dd\u27de\u27df\u27e0\u27e1\u27e2\u27e3\u27e4\u27e5\u27f0\u27f1\u27f2\u27f3\u27f4\u27f5\u27f6\u27f7\u27f8\u27f9\u27fa\u27fb\u27fc\u27fd\u27fe\u27ff\u2900\u2901\u2902\u2903\u2904\u2905\u2906\u2907\u2908\u2909\u290a\u290b\u290c\u290d\u290e\u290f\u2910\u2911\u2912\u2913\u2914\u2915\u2916\u2917\u2918\u2919\u291a\u291b\u291c\u291d\u291e\u291f\u2920\u2921\u2922\u2923\u2924\u2925\u2926\u2927\u2928\u2929\u292a\u292b\u292c\u292d\u292e\u292f\u2930\u2931\u2932\u2933\u2934\u2935\u2936\u2937\u2938\u2939\u293a\u293b\u293c\u293d\u293e\u293f\u2940\u2941\u2942\u2943\u2944\u2945\u2946\u2947\u2948\u2949\u294a\u294b\u294c\u294d\u294e\u294f\u2950\u2951\u2952\u2953\u2954\u2955\u2956\u2957\u2958\u2959\u295a\u295b\u295c\u295d\u295e\u295f\u2960\u2961\u2962\u2963\u2964\u2965\u2966\u2967\u2968\u2969\u296a\u296b\u296c\u296d\u296e\u296f\u2970\u2971\u2972\u2973\u2974\u2975\u2976\u2977\u2978\u2979\u297a\u297b\u297c\u297d\u297e\u297f\u2980\u2981\u2982\u2999\u299a\u299b\u299c\u299d\u299e\u299f\u29a0\u29a1\u29a2\u29a3\u29a4\u29a5\u29a6\u29a7\u29a8\u29a9\u29aa\u29ab\u29ac\u29ad\u29ae\u29af\u29b0\u29b1\u29b2\u29b3\u29b4\u29b5\u29b6\u29b7\u29b8\u29b9\u29ba\u29bb\u29bc\u29bd\u29be\u29bf\u29c0\u29c1\u29c2\u29c3\u29c4\u29c5\u29c6\u29c7\u29c8\u29c9\u29ca\u29cb\u29cc\u29cd\u29ce\u29cf\u29d0\u29d1\u29d2\u29d3\u29d4\u29d5\u29d6\u29d7\u29dc\u29dd\u29de\u29df\u29e0\u29e1\u29e2\u29e3\u29e4\u29e5\u29e6\u29e7\u29e8\u29e9\u29ea\u29eb\u29ec\u29ed\u29ee\u29ef\u29f0\u29f1\u29f2\u29f3\u29f4\u29f5\u29f6\u29f7\u29f8\u29f9\u29fa\u29fb\u29fe\u29ff\u2a00\u2a01\u2a02\u2a03\u2a04\u2a05\u2a06\u2a07\u2a08\u2a09\u2a0a\u2a0b\u2a0c\u2a0d\u2a0e\u2a0f\u2a10\u2a11\u2a12\u2a13\u2a14\u2a15\u2a16\u2a17\u2a18\u2a19\u2a1a\u2a1b\u2a1c\u2a1d\u2a1e\u2a1f\u2a20\u2a21\u2a22\u2a23\u2a24\u2a25\u2a26\u2a27\u2a28\u2a29\u2a2a\u2a2b\u2a2c\u2a2d\u2a2e\u2a2f\u2a30\u2a31\u2a32\u2a33\u2a34\u2a35\u2a36\u2a37\u2a38\u2a39\u2a3a\u2a3b\u2a3c\u2a3d\u2a3e\u2a3f\u2a40\u2a41\u2a42\u2a43\u2a44\u2a45\u2a46\u2a47\u2a48\u2a49\u2a4a\u2a4b\u2a4c\u2a4d\u2a4e\u2a4f\u2a50\u2a51\u2a52\u2a53\u2a54\u2a55\u2a56\u2a57\u2a58\u2a59\u2a5a\u2a5b\u2a5c\u2a5d\u2a5e\u2a5f\u2a60\u2a61\u2a62\u2a63\u2a64\u2a65\u2a66\u2a67\u2a68\u2a69\u2a6a\u2a6b\u2a6c\u2a6d\u2a6e\u2a6f\u2a70\u2a71\u2a72\u2a73\u2a74\u2a75\u2a76\u2a77\u2a78\u2a79\u2a7a\u2a7b\u2a7c\u2a7d\u2a7e\u2a7f\u2a80\u2a81\u2a82\u2a83\u2a84\u2a85\u2a86\u2a87\u2a88\u2a89\u2a8a\u2a8b\u2a8c\u2a8d\u2a8e\u2a8f\u2a90\u2a91\u2a92\u2a93\u2a94\u2a95\u2a96\u2a97\u2a98\u2a99\u2a9a\u2a9b\u2a9c\u2a9d\u2a9e\u2a9f\u2aa0\u2aa1\u2aa2\u2aa3\u2aa4\u2aa5\u2aa6\u2aa7\u2aa8\u2aa9\u2aaa\u2aab\u2aac\u2aad\u2aae\u2aaf\u2ab0\u2ab1\u2ab2\u2ab3\u2ab4\u2ab5\u2ab6\u2ab7\u2ab8\u2ab9\u2aba\u2abb\u2abc\u2abd\u2abe\u2abf\u2ac0\u2ac1\u2ac2\u2ac3\u2ac4\u2ac5\u2ac6\u2ac7\u2ac8\u2ac9\u2aca\u2acb\u2acc\u2acd\u2ace\u2acf\u2ad0\u2ad1\u2ad2\u2ad3\u2ad4\u2ad5\u2ad6\u2ad7\u2ad8\u2ad9\u2ada\u2adb\u2adc\u2add\u2ade\u2adf\u2ae0\u2ae1\u2ae2\u2ae3\u2ae4\u2ae5\u2ae6\u2ae7\u2ae8\u2ae9\u2aea\u2aeb\u2aec\u2aed\u2aee\u2aef\u2af0\u2af1\u2af2\u2af3\u2af4\u2af5\u2af6\u2af7\u2af8\u2af9\u2afa\u2afb\u2afc\u2afd\u2afe\u2aff\ufb29\ufe62\ufe64\ufe65\ufe66\uff0b\uff1c\uff1d\uff1e\uff5c\uff5e\uffe2\uffe9\uffea\uffeb\uffec' + +So = u'\xa6\xa7\xa9\xae\xb0\xb6\u0482\u060e\u060f\u06e9\u06fd\u06fe\u09fa\u0b70\u0bf3\u0bf4\u0bf5\u0bf6\u0bf7\u0bf8\u0bfa\u0f01\u0f02\u0f03\u0f13\u0f14\u0f15\u0f16\u0f17\u0f1a\u0f1b\u0f1c\u0f1d\u0f1e\u0f1f\u0f34\u0f36\u0f38\u0fbe\u0fbf\u0fc0\u0fc1\u0fc2\u0fc3\u0fc4\u0fc5\u0fc7\u0fc8\u0fc9\u0fca\u0fcb\u0fcc\u0fcf\u1360\u1390\u1391\u1392\u1393\u1394\u1395\u1396\u1397\u1398\u1399\u1940\u19e0\u19e1\u19e2\u19e3\u19e4\u19e5\u19e6\u19e7\u19e8\u19e9\u19ea\u19eb\u19ec\u19ed\u19ee\u19ef\u19f0\u19f1\u19f2\u19f3\u19f4\u19f5\u19f6\u19f7\u19f8\u19f9\u19fa\u19fb\u19fc\u19fd\u19fe\u19ff\u2100\u2101\u2103\u2104\u2105\u2106\u2108\u2109\u2114\u2116\u2117\u2118\u211e\u211f\u2120\u2121\u2122\u2123\u2125\u2127\u2129\u212e\u2132\u213a\u213b\u214a\u214c\u2195\u2196\u2197\u2198\u2199\u219c\u219d\u219e\u219f\u21a1\u21a2\u21a4\u21a5\u21a7\u21a8\u21a9\u21aa\u21ab\u21ac\u21ad\u21af\u21b0\u21b1\u21b2\u21b3\u21b4\u21b5\u21b6\u21b7\u21b8\u21b9\u21ba\u21bb\u21bc\u21bd\u21be\u21bf\u21c0\u21c1\u21c2\u21c3\u21c4\u21c5\u21c6\u21c7\u21c8\u21c9\u21ca\u21cb\u21cc\u21cd\u21d0\u21d1\u21d3\u21d5\u21d6\u21d7\u21d8\u21d9\u21da\u21db\u21dc\u21dd\u21de\u21df\u21e0\u21e1\u21e2\u21e3\u21e4\u21e5\u21e6\u21e7\u21e8\u21e9\u21ea\u21eb\u21ec\u21ed\u21ee\u21ef\u21f0\u21f1\u21f2\u21f3\u2300\u2301\u2302\u2303\u2304\u2305\u2306\u2307\u230c\u230d\u230e\u230f\u2310\u2311\u2312\u2313\u2314\u2315\u2316\u2317\u2318\u2319\u231a\u231b\u231c\u231d\u231e\u231f\u2322\u2323\u2324\u2325\u2326\u2327\u2328\u232b\u232c\u232d\u232e\u232f\u2330\u2331\u2332\u2333\u2334\u2335\u2336\u2337\u2338\u2339\u233a\u233b\u233c\u233d\u233e\u233f\u2340\u2341\u2342\u2343\u2344\u2345\u2346\u2347\u2348\u2349\u234a\u234b\u234c\u234d\u234e\u234f\u2350\u2351\u2352\u2353\u2354\u2355\u2356\u2357\u2358\u2359\u235a\u235b\u235c\u235d\u235e\u235f\u2360\u2361\u2362\u2363\u2364\u2365\u2366\u2367\u2368\u2369\u236a\u236b\u236c\u236d\u236e\u236f\u2370\u2371\u2372\u2373\u2374\u2375\u2376\u2377\u2378\u2379\u237a\u237b\u237d\u237e\u237f\u2380\u2381\u2382\u2383\u2384\u2385\u2386\u2387\u2388\u2389\u238a\u238b\u238c\u238d\u238e\u238f\u2390\u2391\u2392\u2393\u2394\u2395\u2396\u2397\u2398\u2399\u239a\u23b7\u23b8\u23b9\u23ba\u23bb\u23bc\u23bd\u23be\u23bf\u23c0\u23c1\u23c2\u23c3\u23c4\u23c5\u23c6\u23c7\u23c8\u23c9\u23ca\u23cb\u23cc\u23cd\u23ce\u23cf\u23d0\u23d1\u23d2\u23d3\u23d4\u23d5\u23d6\u23d7\u23d8\u23d9\u23da\u23db\u2400\u2401\u2402\u2403\u2404\u2405\u2406\u2407\u2408\u2409\u240a\u240b\u240c\u240d\u240e\u240f\u2410\u2411\u2412\u2413\u2414\u2415\u2416\u2417\u2418\u2419\u241a\u241b\u241c\u241d\u241e\u241f\u2420\u2421\u2422\u2423\u2424\u2425\u2426\u2440\u2441\u2442\u2443\u2444\u2445\u2446\u2447\u2448\u2449\u244a\u249c\u249d\u249e\u249f\u24a0\u24a1\u24a2\u24a3\u24a4\u24a5\u24a6\u24a7\u24a8\u24a9\u24aa\u24ab\u24ac\u24ad\u24ae\u24af\u24b0\u24b1\u24b2\u24b3\u24b4\u24b5\u24b6\u24b7\u24b8\u24b9\u24ba\u24bb\u24bc\u24bd\u24be\u24bf\u24c0\u24c1\u24c2\u24c3\u24c4\u24c5\u24c6\u24c7\u24c8\u24c9\u24ca\u24cb\u24cc\u24cd\u24ce\u24cf\u24d0\u24d1\u24d2\u24d3\u24d4\u24d5\u24d6\u24d7\u24d8\u24d9\u24da\u24db\u24dc\u24dd\u24de\u24df\u24e0\u24e1\u24e2\u24e3\u24e4\u24e5\u24e6\u24e7\u24e8\u24e9\u2500\u2501\u2502\u2503\u2504\u2505\u2506\u2507\u2508\u2509\u250a\u250b\u250c\u250d\u250e\u250f\u2510\u2511\u2512\u2513\u2514\u2515\u2516\u2517\u2518\u2519\u251a\u251b\u251c\u251d\u251e\u251f\u2520\u2521\u2522\u2523\u2524\u2525\u2526\u2527\u2528\u2529\u252a\u252b\u252c\u252d\u252e\u252f\u2530\u2531\u2532\u2533\u2534\u2535\u2536\u2537\u2538\u2539\u253a\u253b\u253c\u253d\u253e\u253f\u2540\u2541\u2542\u2543\u2544\u2545\u2546\u2547\u2548\u2549\u254a\u254b\u254c\u254d\u254e\u254f\u2550\u2551\u2552\u2553\u2554\u2555\u2556\u2557\u2558\u2559\u255a\u255b\u255c\u255d\u255e\u255f\u2560\u2561\u2562\u2563\u2564\u2565\u2566\u2567\u2568\u2569\u256a\u256b\u256c\u256d\u256e\u256f\u2570\u2571\u2572\u2573\u2574\u2575\u2576\u2577\u2578\u2579\u257a\u257b\u257c\u257d\u257e\u257f\u2580\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588\u2589\u258a\u258b\u258c\u258d\u258e\u258f\u2590\u2591\u2592\u2593\u2594\u2595\u2596\u2597\u2598\u2599\u259a\u259b\u259c\u259d\u259e\u259f\u25a0\u25a1\u25a2\u25a3\u25a4\u25a5\u25a6\u25a7\u25a8\u25a9\u25aa\u25ab\u25ac\u25ad\u25ae\u25af\u25b0\u25b1\u25b2\u25b3\u25b4\u25b5\u25b6\u25b8\u25b9\u25ba\u25bb\u25bc\u25bd\u25be\u25bf\u25c0\u25c2\u25c3\u25c4\u25c5\u25c6\u25c7\u25c8\u25c9\u25ca\u25cb\u25cc\u25cd\u25ce\u25cf\u25d0\u25d1\u25d2\u25d3\u25d4\u25d5\u25d6\u25d7\u25d8\u25d9\u25da\u25db\u25dc\u25dd\u25de\u25df\u25e0\u25e1\u25e2\u25e3\u25e4\u25e5\u25e6\u25e7\u25e8\u25e9\u25ea\u25eb\u25ec\u25ed\u25ee\u25ef\u25f0\u25f1\u25f2\u25f3\u25f4\u25f5\u25f6\u25f7\u2600\u2601\u2602\u2603\u2604\u2605\u2606\u2607\u2608\u2609\u260a\u260b\u260c\u260d\u260e\u260f\u2610\u2611\u2612\u2613\u2614\u2615\u2616\u2617\u2618\u2619\u261a\u261b\u261c\u261d\u261e\u261f\u2620\u2621\u2622\u2623\u2624\u2625\u2626\u2627\u2628\u2629\u262a\u262b\u262c\u262d\u262e\u262f\u2630\u2631\u2632\u2633\u2634\u2635\u2636\u2637\u2638\u2639\u263a\u263b\u263c\u263d\u263e\u263f\u2640\u2641\u2642\u2643\u2644\u2645\u2646\u2647\u2648\u2649\u264a\u264b\u264c\u264d\u264e\u264f\u2650\u2651\u2652\u2653\u2654\u2655\u2656\u2657\u2658\u2659\u265a\u265b\u265c\u265d\u265e\u265f\u2660\u2661\u2662\u2663\u2664\u2665\u2666\u2667\u2668\u2669\u266a\u266b\u266c\u266d\u266e\u2670\u2671\u2672\u2673\u2674\u2675\u2676\u2677\u2678\u2679\u267a\u267b\u267c\u267d\u267e\u267f\u2680\u2681\u2682\u2683\u2684\u2685\u2686\u2687\u2688\u2689\u268a\u268b\u268c\u268d\u268e\u268f\u2690\u2691\u2692\u2693\u2694\u2695\u2696\u2697\u2698\u2699\u269a\u269b\u269c\u26a0\u26a1\u26a2\u26a3\u26a4\u26a5\u26a6\u26a7\u26a8\u26a9\u26aa\u26ab\u26ac\u26ad\u26ae\u26af\u26b0\u26b1\u2701\u2702\u2703\u2704\u2706\u2707\u2708\u2709\u270c\u270d\u270e\u270f\u2710\u2711\u2712\u2713\u2714\u2715\u2716\u2717\u2718\u2719\u271a\u271b\u271c\u271d\u271e\u271f\u2720\u2721\u2722\u2723\u2724\u2725\u2726\u2727\u2729\u272a\u272b\u272c\u272d\u272e\u272f\u2730\u2731\u2732\u2733\u2734\u2735\u2736\u2737\u2738\u2739\u273a\u273b\u273c\u273d\u273e\u273f\u2740\u2741\u2742\u2743\u2744\u2745\u2746\u2747\u2748\u2749\u274a\u274b\u274d\u274f\u2750\u2751\u2752\u2756\u2758\u2759\u275a\u275b\u275c\u275d\u275e\u2761\u2762\u2763\u2764\u2765\u2766\u2767\u2794\u2798\u2799\u279a\u279b\u279c\u279d\u279e\u279f\u27a0\u27a1\u27a2\u27a3\u27a4\u27a5\u27a6\u27a7\u27a8\u27a9\u27aa\u27ab\u27ac\u27ad\u27ae\u27af\u27b1\u27b2\u27b3\u27b4\u27b5\u27b6\u27b7\u27b8\u27b9\u27ba\u27bb\u27bc\u27bd\u27be\u2800\u2801\u2802\u2803\u2804\u2805\u2806\u2807\u2808\u2809\u280a\u280b\u280c\u280d\u280e\u280f\u2810\u2811\u2812\u2813\u2814\u2815\u2816\u2817\u2818\u2819\u281a\u281b\u281c\u281d\u281e\u281f\u2820\u2821\u2822\u2823\u2824\u2825\u2826\u2827\u2828\u2829\u282a\u282b\u282c\u282d\u282e\u282f\u2830\u2831\u2832\u2833\u2834\u2835\u2836\u2837\u2838\u2839\u283a\u283b\u283c\u283d\u283e\u283f\u2840\u2841\u2842\u2843\u2844\u2845\u2846\u2847\u2848\u2849\u284a\u284b\u284c\u284d\u284e\u284f\u2850\u2851\u2852\u2853\u2854\u2855\u2856\u2857\u2858\u2859\u285a\u285b\u285c\u285d\u285e\u285f\u2860\u2861\u2862\u2863\u2864\u2865\u2866\u2867\u2868\u2869\u286a\u286b\u286c\u286d\u286e\u286f\u2870\u2871\u2872\u2873\u2874\u2875\u2876\u2877\u2878\u2879\u287a\u287b\u287c\u287d\u287e\u287f\u2880\u2881\u2882\u2883\u2884\u2885\u2886\u2887\u2888\u2889\u288a\u288b\u288c\u288d\u288e\u288f\u2890\u2891\u2892\u2893\u2894\u2895\u2896\u2897\u2898\u2899\u289a\u289b\u289c\u289d\u289e\u289f\u28a0\u28a1\u28a2\u28a3\u28a4\u28a5\u28a6\u28a7\u28a8\u28a9\u28aa\u28ab\u28ac\u28ad\u28ae\u28af\u28b0\u28b1\u28b2\u28b3\u28b4\u28b5\u28b6\u28b7\u28b8\u28b9\u28ba\u28bb\u28bc\u28bd\u28be\u28bf\u28c0\u28c1\u28c2\u28c3\u28c4\u28c5\u28c6\u28c7\u28c8\u28c9\u28ca\u28cb\u28cc\u28cd\u28ce\u28cf\u28d0\u28d1\u28d2\u28d3\u28d4\u28d5\u28d6\u28d7\u28d8\u28d9\u28da\u28db\u28dc\u28dd\u28de\u28df\u28e0\u28e1\u28e2\u28e3\u28e4\u28e5\u28e6\u28e7\u28e8\u28e9\u28ea\u28eb\u28ec\u28ed\u28ee\u28ef\u28f0\u28f1\u28f2\u28f3\u28f4\u28f5\u28f6\u28f7\u28f8\u28f9\u28fa\u28fb\u28fc\u28fd\u28fe\u28ff\u2b00\u2b01\u2b02\u2b03\u2b04\u2b05\u2b06\u2b07\u2b08\u2b09\u2b0a\u2b0b\u2b0c\u2b0d\u2b0e\u2b0f\u2b10\u2b11\u2b12\u2b13\u2ce5\u2ce6\u2ce7\u2ce8\u2ce9\u2cea\u2e80\u2e81\u2e82\u2e83\u2e84\u2e85\u2e86\u2e87\u2e88\u2e89\u2e8a\u2e8b\u2e8c\u2e8d\u2e8e\u2e8f\u2e90\u2e91\u2e92\u2e93\u2e94\u2e95\u2e96\u2e97\u2e98\u2e99\u2e9b\u2e9c\u2e9d\u2e9e\u2e9f\u2ea0\u2ea1\u2ea2\u2ea3\u2ea4\u2ea5\u2ea6\u2ea7\u2ea8\u2ea9\u2eaa\u2eab\u2eac\u2ead\u2eae\u2eaf\u2eb0\u2eb1\u2eb2\u2eb3\u2eb4\u2eb5\u2eb6\u2eb7\u2eb8\u2eb9\u2eba\u2ebb\u2ebc\u2ebd\u2ebe\u2ebf\u2ec0\u2ec1\u2ec2\u2ec3\u2ec4\u2ec5\u2ec6\u2ec7\u2ec8\u2ec9\u2eca\u2ecb\u2ecc\u2ecd\u2ece\u2ecf\u2ed0\u2ed1\u2ed2\u2ed3\u2ed4\u2ed5\u2ed6\u2ed7\u2ed8\u2ed9\u2eda\u2edb\u2edc\u2edd\u2ede\u2edf\u2ee0\u2ee1\u2ee2\u2ee3\u2ee4\u2ee5\u2ee6\u2ee7\u2ee8\u2ee9\u2eea\u2eeb\u2eec\u2eed\u2eee\u2eef\u2ef0\u2ef1\u2ef2\u2ef3\u2f00\u2f01\u2f02\u2f03\u2f04\u2f05\u2f06\u2f07\u2f08\u2f09\u2f0a\u2f0b\u2f0c\u2f0d\u2f0e\u2f0f\u2f10\u2f11\u2f12\u2f13\u2f14\u2f15\u2f16\u2f17\u2f18\u2f19\u2f1a\u2f1b\u2f1c\u2f1d\u2f1e\u2f1f\u2f20\u2f21\u2f22\u2f23\u2f24\u2f25\u2f26\u2f27\u2f28\u2f29\u2f2a\u2f2b\u2f2c\u2f2d\u2f2e\u2f2f\u2f30\u2f31\u2f32\u2f33\u2f34\u2f35\u2f36\u2f37\u2f38\u2f39\u2f3a\u2f3b\u2f3c\u2f3d\u2f3e\u2f3f\u2f40\u2f41\u2f42\u2f43\u2f44\u2f45\u2f46\u2f47\u2f48\u2f49\u2f4a\u2f4b\u2f4c\u2f4d\u2f4e\u2f4f\u2f50\u2f51\u2f52\u2f53\u2f54\u2f55\u2f56\u2f57\u2f58\u2f59\u2f5a\u2f5b\u2f5c\u2f5d\u2f5e\u2f5f\u2f60\u2f61\u2f62\u2f63\u2f64\u2f65\u2f66\u2f67\u2f68\u2f69\u2f6a\u2f6b\u2f6c\u2f6d\u2f6e\u2f6f\u2f70\u2f71\u2f72\u2f73\u2f74\u2f75\u2f76\u2f77\u2f78\u2f79\u2f7a\u2f7b\u2f7c\u2f7d\u2f7e\u2f7f\u2f80\u2f81\u2f82\u2f83\u2f84\u2f85\u2f86\u2f87\u2f88\u2f89\u2f8a\u2f8b\u2f8c\u2f8d\u2f8e\u2f8f\u2f90\u2f91\u2f92\u2f93\u2f94\u2f95\u2f96\u2f97\u2f98\u2f99\u2f9a\u2f9b\u2f9c\u2f9d\u2f9e\u2f9f\u2fa0\u2fa1\u2fa2\u2fa3\u2fa4\u2fa5\u2fa6\u2fa7\u2fa8\u2fa9\u2faa\u2fab\u2fac\u2fad\u2fae\u2faf\u2fb0\u2fb1\u2fb2\u2fb3\u2fb4\u2fb5\u2fb6\u2fb7\u2fb8\u2fb9\u2fba\u2fbb\u2fbc\u2fbd\u2fbe\u2fbf\u2fc0\u2fc1\u2fc2\u2fc3\u2fc4\u2fc5\u2fc6\u2fc7\u2fc8\u2fc9\u2fca\u2fcb\u2fcc\u2fcd\u2fce\u2fcf\u2fd0\u2fd1\u2fd2\u2fd3\u2fd4\u2fd5\u2ff0\u2ff1\u2ff2\u2ff3\u2ff4\u2ff5\u2ff6\u2ff7\u2ff8\u2ff9\u2ffa\u2ffb\u3004\u3012\u3013\u3020\u3036\u3037\u303e\u303f\u3190\u3191\u3196\u3197\u3198\u3199\u319a\u319b\u319c\u319d\u319e\u319f\u31c0\u31c1\u31c2\u31c3\u31c4\u31c5\u31c6\u31c7\u31c8\u31c9\u31ca\u31cb\u31cc\u31cd\u31ce\u31cf\u3200\u3201\u3202\u3203\u3204\u3205\u3206\u3207\u3208\u3209\u320a\u320b\u320c\u320d\u320e\u320f\u3210\u3211\u3212\u3213\u3214\u3215\u3216\u3217\u3218\u3219\u321a\u321b\u321c\u321d\u321e\u322a\u322b\u322c\u322d\u322e\u322f\u3230\u3231\u3232\u3233\u3234\u3235\u3236\u3237\u3238\u3239\u323a\u323b\u323c\u323d\u323e\u323f\u3240\u3241\u3242\u3243\u3250\u3260\u3261\u3262\u3263\u3264\u3265\u3266\u3267\u3268\u3269\u326a\u326b\u326c\u326d\u326e\u326f\u3270\u3271\u3272\u3273\u3274\u3275\u3276\u3277\u3278\u3279\u327a\u327b\u327c\u327d\u327e\u327f\u328a\u328b\u328c\u328d\u328e\u328f\u3290\u3291\u3292\u3293\u3294\u3295\u3296\u3297\u3298\u3299\u329a\u329b\u329c\u329d\u329e\u329f\u32a0\u32a1\u32a2\u32a3\u32a4\u32a5\u32a6\u32a7\u32a8\u32a9\u32aa\u32ab\u32ac\u32ad\u32ae\u32af\u32b0\u32c0\u32c1\u32c2\u32c3\u32c4\u32c5\u32c6\u32c7\u32c8\u32c9\u32ca\u32cb\u32cc\u32cd\u32ce\u32cf\u32d0\u32d1\u32d2\u32d3\u32d4\u32d5\u32d6\u32d7\u32d8\u32d9\u32da\u32db\u32dc\u32dd\u32de\u32df\u32e0\u32e1\u32e2\u32e3\u32e4\u32e5\u32e6\u32e7\u32e8\u32e9\u32ea\u32eb\u32ec\u32ed\u32ee\u32ef\u32f0\u32f1\u32f2\u32f3\u32f4\u32f5\u32f6\u32f7\u32f8\u32f9\u32fa\u32fb\u32fc\u32fd\u32fe\u3300\u3301\u3302\u3303\u3304\u3305\u3306\u3307\u3308\u3309\u330a\u330b\u330c\u330d\u330e\u330f\u3310\u3311\u3312\u3313\u3314\u3315\u3316\u3317\u3318\u3319\u331a\u331b\u331c\u331d\u331e\u331f\u3320\u3321\u3322\u3323\u3324\u3325\u3326\u3327\u3328\u3329\u332a\u332b\u332c\u332d\u332e\u332f\u3330\u3331\u3332\u3333\u3334\u3335\u3336\u3337\u3338\u3339\u333a\u333b\u333c\u333d\u333e\u333f\u3340\u3341\u3342\u3343\u3344\u3345\u3346\u3347\u3348\u3349\u334a\u334b\u334c\u334d\u334e\u334f\u3350\u3351\u3352\u3353\u3354\u3355\u3356\u3357\u3358\u3359\u335a\u335b\u335c\u335d\u335e\u335f\u3360\u3361\u3362\u3363\u3364\u3365\u3366\u3367\u3368\u3369\u336a\u336b\u336c\u336d\u336e\u336f\u3370\u3371\u3372\u3373\u3374\u3375\u3376\u3377\u3378\u3379\u337a\u337b\u337c\u337d\u337e\u337f\u3380\u3381\u3382\u3383\u3384\u3385\u3386\u3387\u3388\u3389\u338a\u338b\u338c\u338d\u338e\u338f\u3390\u3391\u3392\u3393\u3394\u3395\u3396\u3397\u3398\u3399\u339a\u339b\u339c\u339d\u339e\u339f\u33a0\u33a1\u33a2\u33a3\u33a4\u33a5\u33a6\u33a7\u33a8\u33a9\u33aa\u33ab\u33ac\u33ad\u33ae\u33af\u33b0\u33b1\u33b2\u33b3\u33b4\u33b5\u33b6\u33b7\u33b8\u33b9\u33ba\u33bb\u33bc\u33bd\u33be\u33bf\u33c0\u33c1\u33c2\u33c3\u33c4\u33c5\u33c6\u33c7\u33c8\u33c9\u33ca\u33cb\u33cc\u33cd\u33ce\u33cf\u33d0\u33d1\u33d2\u33d3\u33d4\u33d5\u33d6\u33d7\u33d8\u33d9\u33da\u33db\u33dc\u33dd\u33de\u33df\u33e0\u33e1\u33e2\u33e3\u33e4\u33e5\u33e6\u33e7\u33e8\u33e9\u33ea\u33eb\u33ec\u33ed\u33ee\u33ef\u33f0\u33f1\u33f2\u33f3\u33f4\u33f5\u33f6\u33f7\u33f8\u33f9\u33fa\u33fb\u33fc\u33fd\u33fe\u33ff\u4dc0\u4dc1\u4dc2\u4dc3\u4dc4\u4dc5\u4dc6\u4dc7\u4dc8\u4dc9\u4dca\u4dcb\u4dcc\u4dcd\u4dce\u4dcf\u4dd0\u4dd1\u4dd2\u4dd3\u4dd4\u4dd5\u4dd6\u4dd7\u4dd8\u4dd9\u4dda\u4ddb\u4ddc\u4ddd\u4dde\u4ddf\u4de0\u4de1\u4de2\u4de3\u4de4\u4de5\u4de6\u4de7\u4de8\u4de9\u4dea\u4deb\u4dec\u4ded\u4dee\u4def\u4df0\u4df1\u4df2\u4df3\u4df4\u4df5\u4df6\u4df7\u4df8\u4df9\u4dfa\u4dfb\u4dfc\u4dfd\u4dfe\u4dff\ua490\ua491\ua492\ua493\ua494\ua495\ua496\ua497\ua498\ua499\ua49a\ua49b\ua49c\ua49d\ua49e\ua49f\ua4a0\ua4a1\ua4a2\ua4a3\ua4a4\ua4a5\ua4a6\ua4a7\ua4a8\ua4a9\ua4aa\ua4ab\ua4ac\ua4ad\ua4ae\ua4af\ua4b0\ua4b1\ua4b2\ua4b3\ua4b4\ua4b5\ua4b6\ua4b7\ua4b8\ua4b9\ua4ba\ua4bb\ua4bc\ua4bd\ua4be\ua4bf\ua4c0\ua4c1\ua4c2\ua4c3\ua4c4\ua4c5\ua4c6\ua828\ua829\ua82a\ua82b\ufdfd\uffe4\uffe8\uffed\uffee\ufffc\ufffd' + +Zl = u'\u2028' + +Zp = u'\u2029' + +Zs = u' \xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000' + +cats = ['Cc', 'Cf', 'Cn', 'Co', 'Cs', 'Ll', 'Lm', 'Lo', 'Lt', 'Lu', 'Mc', 'Me', 'Mn', 'Nd', 'Nl', 'No', 'Pc', 'Pd', 'Pe', 'Pf', 'Pi', 'Po', 'Ps', 'Sc', 'Sk', 'Sm', 'So', 'Zl', 'Zp', 'Zs'] + +def combine(*args): + return u''.join([globals()[cat] for cat in args]) + +xid_start = u'\u0041-\u005A\u005F\u0061-\u007A\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u01BA\u01BB\u01BC-\u01BF\u01C0-\u01C3\u01C4-\u0241\u0250-\u02AF\u02B0-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EE\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03CE\u03D0-\u03F5\u03F7-\u0481\u048A-\u04CE\u04D0-\u04F9\u0500-\u050F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0621-\u063A\u0640\u0641-\u064A\u066E-\u066F\u0671-\u06D3\u06D5\u06E5-\u06E6\u06EE-\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u076D\u0780-\u07A5\u07B1\u0904-\u0939\u093D\u0950\u0958-\u0961\u097D\u0985-\u098C\u098F-\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC-\u09DD\u09DF-\u09E1\u09F0-\u09F1\u0A05-\u0A0A\u0A0F-\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32-\u0A33\u0A35-\u0A36\u0A38-\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2-\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0-\u0AE1\u0B05-\u0B0C\u0B0F-\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32-\u0B33\u0B35-\u0B39\u0B3D\u0B5C-\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99-\u0B9A\u0B9C\u0B9E-\u0B9F\u0BA3-\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C60-\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0-\u0CE1\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D28\u0D2A-\u0D39\u0D60-\u0D61\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E40-\u0E45\u0E46\u0E81-\u0E82\u0E84\u0E87-\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA-\u0EAB\u0EAD-\u0EB0\u0EB2\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDD\u0F00\u0F40-\u0F47\u0F49-\u0F6A\u0F88-\u0F8B\u1000-\u1021\u1023-\u1027\u1029-\u102A\u1050-\u1055\u10A0-\u10C5\u10D0-\u10FA\u10FC\u1100-\u1159\u115F-\u11A2\u11A8-\u11F9\u1200-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u1676\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F0\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1842\u1843\u1844-\u1877\u1880-\u18A8\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19A9\u19C1-\u19C7\u1A00-\u1A16\u1D00-\u1D2B\u1D2C-\u1D61\u1D62-\u1D77\u1D78\u1D79-\u1D9A\u1D9B-\u1DBF\u1E00-\u1E9B\u1EA0-\u1EF9\u1F00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u2094\u2102\u2107\u210A-\u2113\u2115\u2118\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212E\u212F-\u2131\u2133-\u2134\u2135-\u2138\u2139\u213C-\u213F\u2145-\u2149\u2160-\u2183\u2C00-\u2C2E\u2C30-\u2C5E\u2C80-\u2CE4\u2D00-\u2D25\u2D30-\u2D65\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005\u3006\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303A\u303B\u303C\u3041-\u3096\u309D-\u309E\u309F\u30A1-\u30FA\u30FC-\u30FE\u30FF\u3105-\u312C\u3131-\u318E\u31A0-\u31B7\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FBB\uA000-\uA014\uA015\uA016-\uA48C\uA800-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uAC00-\uD7A3\uF900-\uFA2D\uFA30-\uFA6A\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40-\uFB41\uFB43-\uFB44\uFB46-\uFBB1\uFBD3-\uFC5D\uFC64-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDF9\uFE71\uFE73\uFE77\uFE79\uFE7B\uFE7D\uFE7F-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFF6F\uFF70\uFF71-\uFF9D\uFFA0-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC' + +xid_continue = u'\u0030-\u0039\u0041-\u005A\u005F\u0061-\u007A\u00AA\u00B5\u00B7\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u01BA\u01BB\u01BC-\u01BF\u01C0-\u01C3\u01C4-\u0241\u0250-\u02AF\u02B0-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EE\u0300-\u036F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03CE\u03D0-\u03F5\u03F7-\u0481\u0483-\u0486\u048A-\u04CE\u04D0-\u04F9\u0500-\u050F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05B9\u05BB-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u0615\u0621-\u063A\u0640\u0641-\u064A\u064B-\u065E\u0660-\u0669\u066E-\u066F\u0670\u0671-\u06D3\u06D5\u06D6-\u06DC\u06DF-\u06E4\u06E5-\u06E6\u06E7-\u06E8\u06EA-\u06ED\u06EE-\u06EF\u06F0-\u06F9\u06FA-\u06FC\u06FF\u0710\u0711\u0712-\u072F\u0730-\u074A\u074D-\u076D\u0780-\u07A5\u07A6-\u07B0\u07B1\u0901-\u0902\u0903\u0904-\u0939\u093C\u093D\u093E-\u0940\u0941-\u0948\u0949-\u094C\u094D\u0950\u0951-\u0954\u0958-\u0961\u0962-\u0963\u0966-\u096F\u097D\u0981\u0982-\u0983\u0985-\u098C\u098F-\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC\u09BD\u09BE-\u09C0\u09C1-\u09C4\u09C7-\u09C8\u09CB-\u09CC\u09CD\u09CE\u09D7\u09DC-\u09DD\u09DF-\u09E1\u09E2-\u09E3\u09E6-\u09EF\u09F0-\u09F1\u0A01-\u0A02\u0A03\u0A05-\u0A0A\u0A0F-\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32-\u0A33\u0A35-\u0A36\u0A38-\u0A39\u0A3C\u0A3E-\u0A40\u0A41-\u0A42\u0A47-\u0A48\u0A4B-\u0A4D\u0A59-\u0A5C\u0A5E\u0A66-\u0A6F\u0A70-\u0A71\u0A72-\u0A74\u0A81-\u0A82\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2-\u0AB3\u0AB5-\u0AB9\u0ABC\u0ABD\u0ABE-\u0AC0\u0AC1-\u0AC5\u0AC7-\u0AC8\u0AC9\u0ACB-\u0ACC\u0ACD\u0AD0\u0AE0-\u0AE1\u0AE2-\u0AE3\u0AE6-\u0AEF\u0B01\u0B02-\u0B03\u0B05-\u0B0C\u0B0F-\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32-\u0B33\u0B35-\u0B39\u0B3C\u0B3D\u0B3E\u0B3F\u0B40\u0B41-\u0B43\u0B47-\u0B48\u0B4B-\u0B4C\u0B4D\u0B56\u0B57\u0B5C-\u0B5D\u0B5F-\u0B61\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99-\u0B9A\u0B9C\u0B9E-\u0B9F\u0BA3-\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BBF\u0BC0\u0BC1-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCC\u0BCD\u0BD7\u0BE6-\u0BEF\u0C01-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3E-\u0C40\u0C41-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55-\u0C56\u0C60-\u0C61\u0C66-\u0C6F\u0C82-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC\u0CBD\u0CBE\u0CBF\u0CC0-\u0CC4\u0CC6\u0CC7-\u0CC8\u0CCA-\u0CCB\u0CCC-\u0CCD\u0CD5-\u0CD6\u0CDE\u0CE0-\u0CE1\u0CE6-\u0CEF\u0D02-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D28\u0D2A-\u0D39\u0D3E-\u0D40\u0D41-\u0D43\u0D46-\u0D48\u0D4A-\u0D4C\u0D4D\u0D57\u0D60-\u0D61\u0D66-\u0D6F\u0D82-\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD1\u0DD2-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2-\u0DF3\u0E01-\u0E30\u0E31\u0E32-\u0E33\u0E34-\u0E3A\u0E40-\u0E45\u0E46\u0E47-\u0E4E\u0E50-\u0E59\u0E81-\u0E82\u0E84\u0E87-\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA-\u0EAB\u0EAD-\u0EB0\u0EB1\u0EB2-\u0EB3\u0EB4-\u0EB9\u0EBB-\u0EBC\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDD\u0F00\u0F18-\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F3F\u0F40-\u0F47\u0F49-\u0F6A\u0F71-\u0F7E\u0F7F\u0F80-\u0F84\u0F86-\u0F87\u0F88-\u0F8B\u0F90-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1021\u1023-\u1027\u1029-\u102A\u102C\u102D-\u1030\u1031\u1032\u1036-\u1037\u1038\u1039\u1040-\u1049\u1050-\u1055\u1056-\u1057\u1058-\u1059\u10A0-\u10C5\u10D0-\u10FA\u10FC\u1100-\u1159\u115F-\u11A2\u11A8-\u11F9\u1200-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135F\u1369-\u1371\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u1676\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F0\u1700-\u170C\u170E-\u1711\u1712-\u1714\u1720-\u1731\u1732-\u1734\u1740-\u1751\u1752-\u1753\u1760-\u176C\u176E-\u1770\u1772-\u1773\u1780-\u17B3\u17B6\u17B7-\u17BD\u17BE-\u17C5\u17C6\u17C7-\u17C8\u17C9-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1842\u1843\u1844-\u1877\u1880-\u18A8\u18A9\u1900-\u191C\u1920-\u1922\u1923-\u1926\u1927-\u1928\u1929-\u192B\u1930-\u1931\u1932\u1933-\u1938\u1939-\u193B\u1946-\u194F\u1950-\u196D\u1970-\u1974\u1980-\u19A9\u19B0-\u19C0\u19C1-\u19C7\u19C8-\u19C9\u19D0-\u19D9\u1A00-\u1A16\u1A17-\u1A18\u1A19-\u1A1B\u1D00-\u1D2B\u1D2C-\u1D61\u1D62-\u1D77\u1D78\u1D79-\u1D9A\u1D9B-\u1DBF\u1DC0-\u1DC3\u1E00-\u1E9B\u1EA0-\u1EF9\u1F00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u203F-\u2040\u2054\u2071\u207F\u2090-\u2094\u20D0-\u20DC\u20E1\u20E5-\u20EB\u2102\u2107\u210A-\u2113\u2115\u2118\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212E\u212F-\u2131\u2133-\u2134\u2135-\u2138\u2139\u213C-\u213F\u2145-\u2149\u2160-\u2183\u2C00-\u2C2E\u2C30-\u2C5E\u2C80-\u2CE4\u2D00-\u2D25\u2D30-\u2D65\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005\u3006\u3007\u3021-\u3029\u302A-\u302F\u3031-\u3035\u3038-\u303A\u303B\u303C\u3041-\u3096\u3099-\u309A\u309D-\u309E\u309F\u30A1-\u30FA\u30FC-\u30FE\u30FF\u3105-\u312C\u3131-\u318E\u31A0-\u31B7\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FBB\uA000-\uA014\uA015\uA016-\uA48C\uA800-\uA801\uA802\uA803-\uA805\uA806\uA807-\uA80A\uA80B\uA80C-\uA822\uA823-\uA824\uA825-\uA826\uA827\uAC00-\uD7A3\uF900-\uFA2D\uFA30-\uFA6A\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1E\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40-\uFB41\uFB43-\uFB44\uFB46-\uFBB1\uFBD3-\uFC5D\uFC64-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDF9\uFE00-\uFE0F\uFE20-\uFE23\uFE33-\uFE34\uFE4D-\uFE4F\uFE71\uFE73\uFE77\uFE79\uFE7B\uFE7D\uFE7F-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFF6F\uFF70\uFF71-\uFF9D\uFF9E-\uFF9F\uFFA0-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC' + +def allexcept(*args): + newcats = cats[:] + for arg in args: + newcats.remove(arg) + return u''.join([globals()[cat] for cat in newcats]) + +if __name__ == '__main__': + import unicodedata + + categories = {} + + f = open(__file__.rstrip('co')) + try: + content = f.read() + finally: + f.close() + + header = content[:content.find('Cc =')] + footer = content[content.find("def combine("):] + + for code in range(65535): + c = unichr(code) + cat = unicodedata.category(c) + categories.setdefault(cat, []).append(c) + + f = open(__file__, 'w') + f.write(header) + + for cat in sorted(categories): + val = u''.join(categories[cat]) + if cat == 'Cs': + # Jython can't handle isolated surrogates + f.write("""\ +try: + Cs = eval(r"%r") +except UnicodeDecodeError: + Cs = '' # Jython can't handle isolated surrogates\n\n""" % val) + else: + f.write('%s = %r\n\n' % (cat, val)) + f.write('cats = %r\n\n' % sorted(categories.keys())) + + f.write(footer) + f.close() diff --git a/mojo/public/third_party/jinja2/bccache.py b/mojo/public/third_party/jinja2/bccache.py new file mode 100644 index 0000000..f2f9db6 --- /dev/null +++ b/mojo/public/third_party/jinja2/bccache.py @@ -0,0 +1,311 @@ +# -*- coding: utf-8 -*- +""" + jinja2.bccache + ~~~~~~~~~~~~~~ + + This module implements the bytecode cache system Jinja is optionally + using. This is useful if you have very complex template situations and + the compiliation of all those templates slow down your application too + much. + + Situations where this is useful are often forking web applications that + are initialized on the first request. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD. +""" +from os import path, listdir +import sys +import marshal +import tempfile +import fnmatch +from hashlib import sha1 +from jinja2.utils import open_if_exists +from jinja2._compat import BytesIO, pickle, PY2, text_type + + +# marshal works better on 3.x, one hack less required +if not PY2: + marshal_dump = marshal.dump + marshal_load = marshal.load +else: + + def marshal_dump(code, f): + if isinstance(f, file): + marshal.dump(code, f) + else: + f.write(marshal.dumps(code)) + + def marshal_load(f): + if isinstance(f, file): + return marshal.load(f) + return marshal.loads(f.read()) + + +bc_version = 2 + +# magic version used to only change with new jinja versions. With 2.6 +# we change this to also take Python version changes into account. The +# reason for this is that Python tends to segfault if fed earlier bytecode +# versions because someone thought it would be a good idea to reuse opcodes +# or make Python incompatible with earlier versions. +bc_magic = 'j2'.encode('ascii') + \ + pickle.dumps(bc_version, 2) + \ + pickle.dumps((sys.version_info[0] << 24) | sys.version_info[1]) + + +class Bucket(object): + """Buckets are used to store the bytecode for one template. It's created + and initialized by the bytecode cache and passed to the loading functions. + + The buckets get an internal checksum from the cache assigned and use this + to automatically reject outdated cache material. Individual bytecode + cache subclasses don't have to care about cache invalidation. + """ + + def __init__(self, environment, key, checksum): + self.environment = environment + self.key = key + self.checksum = checksum + self.reset() + + def reset(self): + """Resets the bucket (unloads the bytecode).""" + self.code = None + + def load_bytecode(self, f): + """Loads bytecode from a file or file like object.""" + # make sure the magic header is correct + magic = f.read(len(bc_magic)) + if magic != bc_magic: + self.reset() + return + # the source code of the file changed, we need to reload + checksum = pickle.load(f) + if self.checksum != checksum: + self.reset() + return + self.code = marshal_load(f) + + def write_bytecode(self, f): + """Dump the bytecode into the file or file like object passed.""" + if self.code is None: + raise TypeError('can\'t write empty bucket') + f.write(bc_magic) + pickle.dump(self.checksum, f, 2) + marshal_dump(self.code, f) + + def bytecode_from_string(self, string): + """Load bytecode from a string.""" + self.load_bytecode(BytesIO(string)) + + def bytecode_to_string(self): + """Return the bytecode as string.""" + out = BytesIO() + self.write_bytecode(out) + return out.getvalue() + + +class BytecodeCache(object): + """To implement your own bytecode cache you have to subclass this class + and override :meth:`load_bytecode` and :meth:`dump_bytecode`. Both of + these methods are passed a :class:`~jinja2.bccache.Bucket`. + + A very basic bytecode cache that saves the bytecode on the file system:: + + from os import path + + class MyCache(BytecodeCache): + + def __init__(self, directory): + self.directory = directory + + def load_bytecode(self, bucket): + filename = path.join(self.directory, bucket.key) + if path.exists(filename): + with open(filename, 'rb') as f: + bucket.load_bytecode(f) + + def dump_bytecode(self, bucket): + filename = path.join(self.directory, bucket.key) + with open(filename, 'wb') as f: + bucket.write_bytecode(f) + + A more advanced version of a filesystem based bytecode cache is part of + Jinja2. + """ + + def load_bytecode(self, bucket): + """Subclasses have to override this method to load bytecode into a + bucket. If they are not able to find code in the cache for the + bucket, it must not do anything. + """ + raise NotImplementedError() + + def dump_bytecode(self, bucket): + """Subclasses have to override this method to write the bytecode + from a bucket back to the cache. If it unable to do so it must not + fail silently but raise an exception. + """ + raise NotImplementedError() + + def clear(self): + """Clears the cache. This method is not used by Jinja2 but should be + implemented to allow applications to clear the bytecode cache used + by a particular environment. + """ + + def get_cache_key(self, name, filename=None): + """Returns the unique hash key for this template name.""" + hash = sha1(name.encode('utf-8')) + if filename is not None: + filename = '|' + filename + if isinstance(filename, text_type): + filename = filename.encode('utf-8') + hash.update(filename) + return hash.hexdigest() + + def get_source_checksum(self, source): + """Returns a checksum for the source.""" + return sha1(source.encode('utf-8')).hexdigest() + + def get_bucket(self, environment, name, filename, source): + """Return a cache bucket for the given template. All arguments are + mandatory but filename may be `None`. + """ + key = self.get_cache_key(name, filename) + checksum = self.get_source_checksum(source) + bucket = Bucket(environment, key, checksum) + self.load_bytecode(bucket) + return bucket + + def set_bucket(self, bucket): + """Put the bucket into the cache.""" + self.dump_bytecode(bucket) + + +class FileSystemBytecodeCache(BytecodeCache): + """A bytecode cache that stores bytecode on the filesystem. It accepts + two arguments: The directory where the cache items are stored and a + pattern string that is used to build the filename. + + If no directory is specified the system temporary items folder is used. + + The pattern can be used to have multiple separate caches operate on the + same directory. The default pattern is ``'__jinja2_%s.cache'``. ``%s`` + is replaced with the cache key. + + >>> bcc = FileSystemBytecodeCache('/tmp/jinja_cache', '%s.cache') + + This bytecode cache supports clearing of the cache using the clear method. + """ + + def __init__(self, directory=None, pattern='__jinja2_%s.cache'): + if directory is None: + directory = tempfile.gettempdir() + self.directory = directory + self.pattern = pattern + + def _get_cache_filename(self, bucket): + return path.join(self.directory, self.pattern % bucket.key) + + def load_bytecode(self, bucket): + f = open_if_exists(self._get_cache_filename(bucket), 'rb') + if f is not None: + try: + bucket.load_bytecode(f) + finally: + f.close() + + def dump_bytecode(self, bucket): + f = open(self._get_cache_filename(bucket), 'wb') + try: + bucket.write_bytecode(f) + finally: + f.close() + + def clear(self): + # imported lazily here because google app-engine doesn't support + # write access on the file system and the function does not exist + # normally. + from os import remove + files = fnmatch.filter(listdir(self.directory), self.pattern % '*') + for filename in files: + try: + remove(path.join(self.directory, filename)) + except OSError: + pass + + +class MemcachedBytecodeCache(BytecodeCache): + """This class implements a bytecode cache that uses a memcache cache for + storing the information. It does not enforce a specific memcache library + (tummy's memcache or cmemcache) but will accept any class that provides + the minimal interface required. + + Libraries compatible with this class: + + - `werkzeug <http://werkzeug.pocoo.org/>`_.contrib.cache + - `python-memcached <http://www.tummy.com/Community/software/python-memcached/>`_ + - `cmemcache <http://gijsbert.org/cmemcache/>`_ + + (Unfortunately the django cache interface is not compatible because it + does not support storing binary data, only unicode. You can however pass + the underlying cache client to the bytecode cache which is available + as `django.core.cache.cache._client`.) + + The minimal interface for the client passed to the constructor is this: + + .. class:: MinimalClientInterface + + .. method:: set(key, value[, timeout]) + + Stores the bytecode in the cache. `value` is a string and + `timeout` the timeout of the key. If timeout is not provided + a default timeout or no timeout should be assumed, if it's + provided it's an integer with the number of seconds the cache + item should exist. + + .. method:: get(key) + + Returns the value for the cache key. If the item does not + exist in the cache the return value must be `None`. + + The other arguments to the constructor are the prefix for all keys that + is added before the actual cache key and the timeout for the bytecode in + the cache system. We recommend a high (or no) timeout. + + This bytecode cache does not support clearing of used items in the cache. + The clear method is a no-operation function. + + .. versionadded:: 2.7 + Added support for ignoring memcache errors through the + `ignore_memcache_errors` parameter. + """ + + def __init__(self, client, prefix='jinja2/bytecode/', timeout=None, + ignore_memcache_errors=True): + self.client = client + self.prefix = prefix + self.timeout = timeout + self.ignore_memcache_errors = ignore_memcache_errors + + def load_bytecode(self, bucket): + try: + code = self.client.get(self.prefix + bucket.key) + except Exception: + if not self.ignore_memcache_errors: + raise + code = None + if code is not None: + bucket.bytecode_from_string(code) + + def dump_bytecode(self, bucket): + args = (self.prefix + bucket.key, bucket.bytecode_to_string()) + if self.timeout is not None: + args += (self.timeout,) + try: + self.client.set(*args) + except Exception: + if not self.ignore_memcache_errors: + raise diff --git a/mojo/public/third_party/jinja2/compiler.py b/mojo/public/third_party/jinja2/compiler.py new file mode 100644 index 0000000..75a60b8 --- /dev/null +++ b/mojo/public/third_party/jinja2/compiler.py @@ -0,0 +1,1640 @@ +# -*- coding: utf-8 -*- +""" + jinja2.compiler + ~~~~~~~~~~~~~~~ + + Compiles nodes into python code. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +from itertools import chain +from copy import deepcopy +from keyword import iskeyword as is_python_keyword +from jinja2 import nodes +from jinja2.nodes import EvalContext +from jinja2.visitor import NodeVisitor +from jinja2.exceptions import TemplateAssertionError +from jinja2.utils import Markup, concat, escape +from jinja2._compat import range_type, next, text_type, string_types, \ + iteritems, NativeStringIO, imap + + +operators = { + 'eq': '==', + 'ne': '!=', + 'gt': '>', + 'gteq': '>=', + 'lt': '<', + 'lteq': '<=', + 'in': 'in', + 'notin': 'not in' +} + +# what method to iterate over items do we want to use for dict iteration +# in generated code? on 2.x let's go with iteritems, on 3.x with items +if hasattr(dict, 'iteritems'): + dict_item_iter = 'iteritems' +else: + dict_item_iter = 'items' + + +# does if 0: dummy(x) get us x into the scope? +def unoptimize_before_dead_code(): + x = 42 + def f(): + if 0: dummy(x) + return f + +# The getattr is necessary for pypy which does not set this attribute if +# no closure is on the function +unoptimize_before_dead_code = bool( + getattr(unoptimize_before_dead_code(), '__closure__', None)) + + +def generate(node, environment, name, filename, stream=None, + defer_init=False): + """Generate the python source for a node tree.""" + if not isinstance(node, nodes.Template): + raise TypeError('Can\'t compile non template nodes') + generator = CodeGenerator(environment, name, filename, stream, defer_init) + generator.visit(node) + if stream is None: + return generator.stream.getvalue() + + +def has_safe_repr(value): + """Does the node have a safe representation?""" + if value is None or value is NotImplemented or value is Ellipsis: + return True + if isinstance(value, (bool, int, float, complex, range_type, + Markup) + string_types): + return True + if isinstance(value, (tuple, list, set, frozenset)): + for item in value: + if not has_safe_repr(item): + return False + return True + elif isinstance(value, dict): + for key, value in iteritems(value): + if not has_safe_repr(key): + return False + if not has_safe_repr(value): + return False + return True + return False + + +def find_undeclared(nodes, names): + """Check if the names passed are accessed undeclared. The return value + is a set of all the undeclared names from the sequence of names found. + """ + visitor = UndeclaredNameVisitor(names) + try: + for node in nodes: + visitor.visit(node) + except VisitorExit: + pass + return visitor.undeclared + + +class Identifiers(object): + """Tracks the status of identifiers in frames.""" + + def __init__(self): + # variables that are known to be declared (probably from outer + # frames or because they are special for the frame) + self.declared = set() + + # undeclared variables from outer scopes + self.outer_undeclared = set() + + # names that are accessed without being explicitly declared by + # this one or any of the outer scopes. Names can appear both in + # declared and undeclared. + self.undeclared = set() + + # names that are declared locally + self.declared_locally = set() + + # names that are declared by parameters + self.declared_parameter = set() + + def add_special(self, name): + """Register a special name like `loop`.""" + self.undeclared.discard(name) + self.declared.add(name) + + def is_declared(self, name): + """Check if a name is declared in this or an outer scope.""" + if name in self.declared_locally or name in self.declared_parameter: + return True + return name in self.declared + + def copy(self): + return deepcopy(self) + + +class Frame(object): + """Holds compile time information for us.""" + + def __init__(self, eval_ctx, parent=None): + self.eval_ctx = eval_ctx + self.identifiers = Identifiers() + + # a toplevel frame is the root + soft frames such as if conditions. + self.toplevel = False + + # the root frame is basically just the outermost frame, so no if + # conditions. This information is used to optimize inheritance + # situations. + self.rootlevel = False + + # in some dynamic inheritance situations the compiler needs to add + # write tests around output statements. + self.require_output_check = parent and parent.require_output_check + + # inside some tags we are using a buffer rather than yield statements. + # this for example affects {% filter %} or {% macro %}. If a frame + # is buffered this variable points to the name of the list used as + # buffer. + self.buffer = None + + # the name of the block we're in, otherwise None. + self.block = parent and parent.block or None + + # a set of actually assigned names + self.assigned_names = set() + + # the parent of this frame + self.parent = parent + + if parent is not None: + self.identifiers.declared.update( + parent.identifiers.declared | + parent.identifiers.declared_parameter | + parent.assigned_names + ) + self.identifiers.outer_undeclared.update( + parent.identifiers.undeclared - + self.identifiers.declared + ) + self.buffer = parent.buffer + + def copy(self): + """Create a copy of the current one.""" + rv = object.__new__(self.__class__) + rv.__dict__.update(self.__dict__) + rv.identifiers = object.__new__(self.identifiers.__class__) + rv.identifiers.__dict__.update(self.identifiers.__dict__) + return rv + + def inspect(self, nodes): + """Walk the node and check for identifiers. If the scope is hard (eg: + enforce on a python level) overrides from outer scopes are tracked + differently. + """ + visitor = FrameIdentifierVisitor(self.identifiers) + for node in nodes: + visitor.visit(node) + + def find_shadowed(self, extra=()): + """Find all the shadowed names. extra is an iterable of variables + that may be defined with `add_special` which may occour scoped. + """ + i = self.identifiers + return (i.declared | i.outer_undeclared) & \ + (i.declared_locally | i.declared_parameter) | \ + set(x for x in extra if i.is_declared(x)) + + def inner(self): + """Return an inner frame.""" + return Frame(self.eval_ctx, self) + + def soft(self): + """Return a soft frame. A soft frame may not be modified as + standalone thing as it shares the resources with the frame it + was created of, but it's not a rootlevel frame any longer. + """ + rv = self.copy() + rv.rootlevel = False + return rv + + __copy__ = copy + + +class VisitorExit(RuntimeError): + """Exception used by the `UndeclaredNameVisitor` to signal a stop.""" + + +class DependencyFinderVisitor(NodeVisitor): + """A visitor that collects filter and test calls.""" + + def __init__(self): + self.filters = set() + self.tests = set() + + def visit_Filter(self, node): + self.generic_visit(node) + self.filters.add(node.name) + + def visit_Test(self, node): + self.generic_visit(node) + self.tests.add(node.name) + + def visit_Block(self, node): + """Stop visiting at blocks.""" + + +class UndeclaredNameVisitor(NodeVisitor): + """A visitor that checks if a name is accessed without being + declared. This is different from the frame visitor as it will + not stop at closure frames. + """ + + def __init__(self, names): + self.names = set(names) + self.undeclared = set() + + def visit_Name(self, node): + if node.ctx == 'load' and node.name in self.names: + self.undeclared.add(node.name) + if self.undeclared == self.names: + raise VisitorExit() + else: + self.names.discard(node.name) + + def visit_Block(self, node): + """Stop visiting a blocks.""" + + +class FrameIdentifierVisitor(NodeVisitor): + """A visitor for `Frame.inspect`.""" + + def __init__(self, identifiers): + self.identifiers = identifiers + + def visit_Name(self, node): + """All assignments to names go through this function.""" + if node.ctx == 'store': + self.identifiers.declared_locally.add(node.name) + elif node.ctx == 'param': + self.identifiers.declared_parameter.add(node.name) + elif node.ctx == 'load' and not \ + self.identifiers.is_declared(node.name): + self.identifiers.undeclared.add(node.name) + + def visit_If(self, node): + self.visit(node.test) + real_identifiers = self.identifiers + + old_names = real_identifiers.declared_locally | \ + real_identifiers.declared_parameter + + def inner_visit(nodes): + if not nodes: + return set() + self.identifiers = real_identifiers.copy() + for subnode in nodes: + self.visit(subnode) + rv = self.identifiers.declared_locally - old_names + # we have to remember the undeclared variables of this branch + # because we will have to pull them. + real_identifiers.undeclared.update(self.identifiers.undeclared) + self.identifiers = real_identifiers + return rv + + body = inner_visit(node.body) + else_ = inner_visit(node.else_ or ()) + + # the differences between the two branches are also pulled as + # undeclared variables + real_identifiers.undeclared.update(body.symmetric_difference(else_) - + real_identifiers.declared) + + # remember those that are declared. + real_identifiers.declared_locally.update(body | else_) + + def visit_Macro(self, node): + self.identifiers.declared_locally.add(node.name) + + def visit_Import(self, node): + self.generic_visit(node) + self.identifiers.declared_locally.add(node.target) + + def visit_FromImport(self, node): + self.generic_visit(node) + for name in node.names: + if isinstance(name, tuple): + self.identifiers.declared_locally.add(name[1]) + else: + self.identifiers.declared_locally.add(name) + + def visit_Assign(self, node): + """Visit assignments in the correct order.""" + self.visit(node.node) + self.visit(node.target) + + def visit_For(self, node): + """Visiting stops at for blocks. However the block sequence + is visited as part of the outer scope. + """ + self.visit(node.iter) + + def visit_CallBlock(self, node): + self.visit(node.call) + + def visit_FilterBlock(self, node): + self.visit(node.filter) + + def visit_Scope(self, node): + """Stop visiting at scopes.""" + + def visit_Block(self, node): + """Stop visiting at blocks.""" + + +class CompilerExit(Exception): + """Raised if the compiler encountered a situation where it just + doesn't make sense to further process the code. Any block that + raises such an exception is not further processed. + """ + + +class CodeGenerator(NodeVisitor): + + def __init__(self, environment, name, filename, stream=None, + defer_init=False): + if stream is None: + stream = NativeStringIO() + self.environment = environment + self.name = name + self.filename = filename + self.stream = stream + self.created_block_context = False + self.defer_init = defer_init + + # aliases for imports + self.import_aliases = {} + + # a registry for all blocks. Because blocks are moved out + # into the global python scope they are registered here + self.blocks = {} + + # the number of extends statements so far + self.extends_so_far = 0 + + # some templates have a rootlevel extends. In this case we + # can safely assume that we're a child template and do some + # more optimizations. + self.has_known_extends = False + + # the current line number + self.code_lineno = 1 + + # registry of all filters and tests (global, not block local) + self.tests = {} + self.filters = {} + + # the debug information + self.debug_info = [] + self._write_debug_info = None + + # the number of new lines before the next write() + self._new_lines = 0 + + # the line number of the last written statement + self._last_line = 0 + + # true if nothing was written so far. + self._first_write = True + + # used by the `temporary_identifier` method to get new + # unique, temporary identifier + self._last_identifier = 0 + + # the current indentation + self._indentation = 0 + + # -- Various compilation helpers + + def fail(self, msg, lineno): + """Fail with a :exc:`TemplateAssertionError`.""" + raise TemplateAssertionError(msg, lineno, self.name, self.filename) + + def temporary_identifier(self): + """Get a new unique identifier.""" + self._last_identifier += 1 + return 't_%d' % self._last_identifier + + def buffer(self, frame): + """Enable buffering for the frame from that point onwards.""" + frame.buffer = self.temporary_identifier() + self.writeline('%s = []' % frame.buffer) + + def return_buffer_contents(self, frame): + """Return the buffer contents of the frame.""" + if frame.eval_ctx.volatile: + self.writeline('if context.eval_ctx.autoescape:') + self.indent() + self.writeline('return Markup(concat(%s))' % frame.buffer) + self.outdent() + self.writeline('else:') + self.indent() + self.writeline('return concat(%s)' % frame.buffer) + self.outdent() + elif frame.eval_ctx.autoescape: + self.writeline('return Markup(concat(%s))' % frame.buffer) + else: + self.writeline('return concat(%s)' % frame.buffer) + + def indent(self): + """Indent by one.""" + self._indentation += 1 + + def outdent(self, step=1): + """Outdent by step.""" + self._indentation -= step + + def start_write(self, frame, node=None): + """Yield or write into the frame buffer.""" + if frame.buffer is None: + self.writeline('yield ', node) + else: + self.writeline('%s.append(' % frame.buffer, node) + + def end_write(self, frame): + """End the writing process started by `start_write`.""" + if frame.buffer is not None: + self.write(')') + + def simple_write(self, s, frame, node=None): + """Simple shortcut for start_write + write + end_write.""" + self.start_write(frame, node) + self.write(s) + self.end_write(frame) + + def blockvisit(self, nodes, frame): + """Visit a list of nodes as block in a frame. If the current frame + is no buffer a dummy ``if 0: yield None`` is written automatically + unless the force_generator parameter is set to False. + """ + if frame.buffer is None: + self.writeline('if 0: yield None') + else: + self.writeline('pass') + try: + for node in nodes: + self.visit(node, frame) + except CompilerExit: + pass + + def write(self, x): + """Write a string into the output stream.""" + if self._new_lines: + if not self._first_write: + self.stream.write('\n' * self._new_lines) + self.code_lineno += self._new_lines + if self._write_debug_info is not None: + self.debug_info.append((self._write_debug_info, + self.code_lineno)) + self._write_debug_info = None + self._first_write = False + self.stream.write(' ' * self._indentation) + self._new_lines = 0 + self.stream.write(x) + + def writeline(self, x, node=None, extra=0): + """Combination of newline and write.""" + self.newline(node, extra) + self.write(x) + + def newline(self, node=None, extra=0): + """Add one or more newlines before the next write.""" + self._new_lines = max(self._new_lines, 1 + extra) + if node is not None and node.lineno != self._last_line: + self._write_debug_info = node.lineno + self._last_line = node.lineno + + def signature(self, node, frame, extra_kwargs=None): + """Writes a function call to the stream for the current node. + A leading comma is added automatically. The extra keyword + arguments may not include python keywords otherwise a syntax + error could occour. The extra keyword arguments should be given + as python dict. + """ + # if any of the given keyword arguments is a python keyword + # we have to make sure that no invalid call is created. + kwarg_workaround = False + for kwarg in chain((x.key for x in node.kwargs), extra_kwargs or ()): + if is_python_keyword(kwarg): + kwarg_workaround = True + break + + for arg in node.args: + self.write(', ') + self.visit(arg, frame) + + if not kwarg_workaround: + for kwarg in node.kwargs: + self.write(', ') + self.visit(kwarg, frame) + if extra_kwargs is not None: + for key, value in iteritems(extra_kwargs): + self.write(', %s=%s' % (key, value)) + if node.dyn_args: + self.write(', *') + self.visit(node.dyn_args, frame) + + if kwarg_workaround: + if node.dyn_kwargs is not None: + self.write(', **dict({') + else: + self.write(', **{') + for kwarg in node.kwargs: + self.write('%r: ' % kwarg.key) + self.visit(kwarg.value, frame) + self.write(', ') + if extra_kwargs is not None: + for key, value in iteritems(extra_kwargs): + self.write('%r: %s, ' % (key, value)) + if node.dyn_kwargs is not None: + self.write('}, **') + self.visit(node.dyn_kwargs, frame) + self.write(')') + else: + self.write('}') + + elif node.dyn_kwargs is not None: + self.write(', **') + self.visit(node.dyn_kwargs, frame) + + def pull_locals(self, frame): + """Pull all the references identifiers into the local scope.""" + for name in frame.identifiers.undeclared: + self.writeline('l_%s = context.resolve(%r)' % (name, name)) + + def pull_dependencies(self, nodes): + """Pull all the dependencies.""" + visitor = DependencyFinderVisitor() + for node in nodes: + visitor.visit(node) + for dependency in 'filters', 'tests': + mapping = getattr(self, dependency) + for name in getattr(visitor, dependency): + if name not in mapping: + mapping[name] = self.temporary_identifier() + self.writeline('%s = environment.%s[%r]' % + (mapping[name], dependency, name)) + + def unoptimize_scope(self, frame): + """Disable Python optimizations for the frame.""" + # XXX: this is not that nice but it has no real overhead. It + # mainly works because python finds the locals before dead code + # is removed. If that breaks we have to add a dummy function + # that just accepts the arguments and does nothing. + if frame.identifiers.declared: + self.writeline('%sdummy(%s)' % ( + unoptimize_before_dead_code and 'if 0: ' or '', + ', '.join('l_' + name for name in frame.identifiers.declared) + )) + + def push_scope(self, frame, extra_vars=()): + """This function returns all the shadowed variables in a dict + in the form name: alias and will write the required assignments + into the current scope. No indentation takes place. + + This also predefines locally declared variables from the loop + body because under some circumstances it may be the case that + + `extra_vars` is passed to `Frame.find_shadowed`. + """ + aliases = {} + for name in frame.find_shadowed(extra_vars): + aliases[name] = ident = self.temporary_identifier() + self.writeline('%s = l_%s' % (ident, name)) + to_declare = set() + for name in frame.identifiers.declared_locally: + if name not in aliases: + to_declare.add('l_' + name) + if to_declare: + self.writeline(' = '.join(to_declare) + ' = missing') + return aliases + + def pop_scope(self, aliases, frame): + """Restore all aliases and delete unused variables.""" + for name, alias in iteritems(aliases): + self.writeline('l_%s = %s' % (name, alias)) + to_delete = set() + for name in frame.identifiers.declared_locally: + if name not in aliases: + to_delete.add('l_' + name) + if to_delete: + # we cannot use the del statement here because enclosed + # scopes can trigger a SyntaxError: + # a = 42; b = lambda: a; del a + self.writeline(' = '.join(to_delete) + ' = missing') + + def function_scoping(self, node, frame, children=None, + find_special=True): + """In Jinja a few statements require the help of anonymous + functions. Those are currently macros and call blocks and in + the future also recursive loops. As there is currently + technical limitation that doesn't allow reading and writing a + variable in a scope where the initial value is coming from an + outer scope, this function tries to fall back with a common + error message. Additionally the frame passed is modified so + that the argumetns are collected and callers are looked up. + + This will return the modified frame. + """ + # we have to iterate twice over it, make sure that works + if children is None: + children = node.iter_child_nodes() + children = list(children) + func_frame = frame.inner() + func_frame.inspect(children) + + # variables that are undeclared (accessed before declaration) and + # declared locally *and* part of an outside scope raise a template + # assertion error. Reason: we can't generate reasonable code from + # it without aliasing all the variables. + # this could be fixed in Python 3 where we have the nonlocal + # keyword or if we switch to bytecode generation + overridden_closure_vars = ( + func_frame.identifiers.undeclared & + func_frame.identifiers.declared & + (func_frame.identifiers.declared_locally | + func_frame.identifiers.declared_parameter) + ) + if overridden_closure_vars: + self.fail('It\'s not possible to set and access variables ' + 'derived from an outer scope! (affects: %s)' % + ', '.join(sorted(overridden_closure_vars)), node.lineno) + + # remove variables from a closure from the frame's undeclared + # identifiers. + func_frame.identifiers.undeclared -= ( + func_frame.identifiers.undeclared & + func_frame.identifiers.declared + ) + + # no special variables for this scope, abort early + if not find_special: + return func_frame + + func_frame.accesses_kwargs = False + func_frame.accesses_varargs = False + func_frame.accesses_caller = False + func_frame.arguments = args = ['l_' + x.name for x in node.args] + + undeclared = find_undeclared(children, ('caller', 'kwargs', 'varargs')) + + if 'caller' in undeclared: + func_frame.accesses_caller = True + func_frame.identifiers.add_special('caller') + args.append('l_caller') + if 'kwargs' in undeclared: + func_frame.accesses_kwargs = True + func_frame.identifiers.add_special('kwargs') + args.append('l_kwargs') + if 'varargs' in undeclared: + func_frame.accesses_varargs = True + func_frame.identifiers.add_special('varargs') + args.append('l_varargs') + return func_frame + + def macro_body(self, node, frame, children=None): + """Dump the function def of a macro or call block.""" + frame = self.function_scoping(node, frame, children) + # macros are delayed, they never require output checks + frame.require_output_check = False + args = frame.arguments + # XXX: this is an ugly fix for the loop nesting bug + # (tests.test_old_bugs.test_loop_call_bug). This works around + # a identifier nesting problem we have in general. It's just more + # likely to happen in loops which is why we work around it. The + # real solution would be "nonlocal" all the identifiers that are + # leaking into a new python frame and might be used both unassigned + # and assigned. + if 'loop' in frame.identifiers.declared: + args = args + ['l_loop=l_loop'] + self.writeline('def macro(%s):' % ', '.join(args), node) + self.indent() + self.buffer(frame) + self.pull_locals(frame) + self.blockvisit(node.body, frame) + self.return_buffer_contents(frame) + self.outdent() + return frame + + def macro_def(self, node, frame): + """Dump the macro definition for the def created by macro_body.""" + arg_tuple = ', '.join(repr(x.name) for x in node.args) + name = getattr(node, 'name', None) + if len(node.args) == 1: + arg_tuple += ',' + self.write('Macro(environment, macro, %r, (%s), (' % + (name, arg_tuple)) + for arg in node.defaults: + self.visit(arg, frame) + self.write(', ') + self.write('), %r, %r, %r)' % ( + bool(frame.accesses_kwargs), + bool(frame.accesses_varargs), + bool(frame.accesses_caller) + )) + + def position(self, node): + """Return a human readable position for the node.""" + rv = 'line %d' % node.lineno + if self.name is not None: + rv += ' in ' + repr(self.name) + return rv + + # -- Statement Visitors + + def visit_Template(self, node, frame=None): + assert frame is None, 'no root frame allowed' + eval_ctx = EvalContext(self.environment, self.name) + + from jinja2.runtime import __all__ as exported + self.writeline('from __future__ import division') + self.writeline('from jinja2.runtime import ' + ', '.join(exported)) + if not unoptimize_before_dead_code: + self.writeline('dummy = lambda *x: None') + + # if we want a deferred initialization we cannot move the + # environment into a local name + envenv = not self.defer_init and ', environment=environment' or '' + + # do we have an extends tag at all? If not, we can save some + # overhead by just not processing any inheritance code. + have_extends = node.find(nodes.Extends) is not None + + # find all blocks + for block in node.find_all(nodes.Block): + if block.name in self.blocks: + self.fail('block %r defined twice' % block.name, block.lineno) + self.blocks[block.name] = block + + # find all imports and import them + for import_ in node.find_all(nodes.ImportedName): + if import_.importname not in self.import_aliases: + imp = import_.importname + self.import_aliases[imp] = alias = self.temporary_identifier() + if '.' in imp: + module, obj = imp.rsplit('.', 1) + self.writeline('from %s import %s as %s' % + (module, obj, alias)) + else: + self.writeline('import %s as %s' % (imp, alias)) + + # add the load name + self.writeline('name = %r' % self.name) + + # generate the root render function. + self.writeline('def root(context%s):' % envenv, extra=1) + + # process the root + frame = Frame(eval_ctx) + frame.inspect(node.body) + frame.toplevel = frame.rootlevel = True + frame.require_output_check = have_extends and not self.has_known_extends + self.indent() + if have_extends: + self.writeline('parent_template = None') + if 'self' in find_undeclared(node.body, ('self',)): + frame.identifiers.add_special('self') + self.writeline('l_self = TemplateReference(context)') + self.pull_locals(frame) + self.pull_dependencies(node.body) + self.blockvisit(node.body, frame) + self.outdent() + + # make sure that the parent root is called. + if have_extends: + if not self.has_known_extends: + self.indent() + self.writeline('if parent_template is not None:') + self.indent() + self.writeline('for event in parent_template.' + 'root_render_func(context):') + self.indent() + self.writeline('yield event') + self.outdent(2 + (not self.has_known_extends)) + + # at this point we now have the blocks collected and can visit them too. + for name, block in iteritems(self.blocks): + block_frame = Frame(eval_ctx) + block_frame.inspect(block.body) + block_frame.block = name + self.writeline('def block_%s(context%s):' % (name, envenv), + block, 1) + self.indent() + undeclared = find_undeclared(block.body, ('self', 'super')) + if 'self' in undeclared: + block_frame.identifiers.add_special('self') + self.writeline('l_self = TemplateReference(context)') + if 'super' in undeclared: + block_frame.identifiers.add_special('super') + self.writeline('l_super = context.super(%r, ' + 'block_%s)' % (name, name)) + self.pull_locals(block_frame) + self.pull_dependencies(block.body) + self.blockvisit(block.body, block_frame) + self.outdent() + + self.writeline('blocks = {%s}' % ', '.join('%r: block_%s' % (x, x) + for x in self.blocks), + extra=1) + + # add a function that returns the debug info + self.writeline('debug_info = %r' % '&'.join('%s=%s' % x for x + in self.debug_info)) + + def visit_Block(self, node, frame): + """Call a block and register it for the template.""" + level = 1 + if frame.toplevel: + # if we know that we are a child template, there is no need to + # check if we are one + if self.has_known_extends: + return + if self.extends_so_far > 0: + self.writeline('if parent_template is None:') + self.indent() + level += 1 + context = node.scoped and 'context.derived(locals())' or 'context' + self.writeline('for event in context.blocks[%r][0](%s):' % ( + node.name, context), node) + self.indent() + self.simple_write('event', frame) + self.outdent(level) + + def visit_Extends(self, node, frame): + """Calls the extender.""" + if not frame.toplevel: + self.fail('cannot use extend from a non top-level scope', + node.lineno) + + # if the number of extends statements in general is zero so + # far, we don't have to add a check if something extended + # the template before this one. + if self.extends_so_far > 0: + + # if we have a known extends we just add a template runtime + # error into the generated code. We could catch that at compile + # time too, but i welcome it not to confuse users by throwing the + # same error at different times just "because we can". + if not self.has_known_extends: + self.writeline('if parent_template is not None:') + self.indent() + self.writeline('raise TemplateRuntimeError(%r)' % + 'extended multiple times') + + # if we have a known extends already we don't need that code here + # as we know that the template execution will end here. + if self.has_known_extends: + raise CompilerExit() + else: + self.outdent() + + self.writeline('parent_template = environment.get_template(', node) + self.visit(node.template, frame) + self.write(', %r)' % self.name) + self.writeline('for name, parent_block in parent_template.' + 'blocks.%s():' % dict_item_iter) + self.indent() + self.writeline('context.blocks.setdefault(name, []).' + 'append(parent_block)') + self.outdent() + + # if this extends statement was in the root level we can take + # advantage of that information and simplify the generated code + # in the top level from this point onwards + if frame.rootlevel: + self.has_known_extends = True + + # and now we have one more + self.extends_so_far += 1 + + def visit_Include(self, node, frame): + """Handles includes.""" + if node.with_context: + self.unoptimize_scope(frame) + if node.ignore_missing: + self.writeline('try:') + self.indent() + + func_name = 'get_or_select_template' + if isinstance(node.template, nodes.Const): + if isinstance(node.template.value, string_types): + func_name = 'get_template' + elif isinstance(node.template.value, (tuple, list)): + func_name = 'select_template' + elif isinstance(node.template, (nodes.Tuple, nodes.List)): + func_name = 'select_template' + + self.writeline('template = environment.%s(' % func_name, node) + self.visit(node.template, frame) + self.write(', %r)' % self.name) + if node.ignore_missing: + self.outdent() + self.writeline('except TemplateNotFound:') + self.indent() + self.writeline('pass') + self.outdent() + self.writeline('else:') + self.indent() + + if node.with_context: + self.writeline('for event in template.root_render_func(' + 'template.new_context(context.parent, True, ' + 'locals())):') + else: + self.writeline('for event in template.module._body_stream:') + + self.indent() + self.simple_write('event', frame) + self.outdent() + + if node.ignore_missing: + self.outdent() + + def visit_Import(self, node, frame): + """Visit regular imports.""" + if node.with_context: + self.unoptimize_scope(frame) + self.writeline('l_%s = ' % node.target, node) + if frame.toplevel: + self.write('context.vars[%r] = ' % node.target) + self.write('environment.get_template(') + self.visit(node.template, frame) + self.write(', %r).' % self.name) + if node.with_context: + self.write('make_module(context.parent, True, locals())') + else: + self.write('module') + if frame.toplevel and not node.target.startswith('_'): + self.writeline('context.exported_vars.discard(%r)' % node.target) + frame.assigned_names.add(node.target) + + def visit_FromImport(self, node, frame): + """Visit named imports.""" + self.newline(node) + self.write('included_template = environment.get_template(') + self.visit(node.template, frame) + self.write(', %r).' % self.name) + if node.with_context: + self.write('make_module(context.parent, True)') + else: + self.write('module') + + var_names = [] + discarded_names = [] + for name in node.names: + if isinstance(name, tuple): + name, alias = name + else: + alias = name + self.writeline('l_%s = getattr(included_template, ' + '%r, missing)' % (alias, name)) + self.writeline('if l_%s is missing:' % alias) + self.indent() + self.writeline('l_%s = environment.undefined(%r %% ' + 'included_template.__name__, ' + 'name=%r)' % + (alias, 'the template %%r (imported on %s) does ' + 'not export the requested name %s' % ( + self.position(node), + repr(name) + ), name)) + self.outdent() + if frame.toplevel: + var_names.append(alias) + if not alias.startswith('_'): + discarded_names.append(alias) + frame.assigned_names.add(alias) + + if var_names: + if len(var_names) == 1: + name = var_names[0] + self.writeline('context.vars[%r] = l_%s' % (name, name)) + else: + self.writeline('context.vars.update({%s})' % ', '.join( + '%r: l_%s' % (name, name) for name in var_names + )) + if discarded_names: + if len(discarded_names) == 1: + self.writeline('context.exported_vars.discard(%r)' % + discarded_names[0]) + else: + self.writeline('context.exported_vars.difference_' + 'update((%s))' % ', '.join(imap(repr, discarded_names))) + + def visit_For(self, node, frame): + # when calculating the nodes for the inner frame we have to exclude + # the iterator contents from it + children = node.iter_child_nodes(exclude=('iter',)) + if node.recursive: + loop_frame = self.function_scoping(node, frame, children, + find_special=False) + else: + loop_frame = frame.inner() + loop_frame.inspect(children) + + # try to figure out if we have an extended loop. An extended loop + # is necessary if the loop is in recursive mode if the special loop + # variable is accessed in the body. + extended_loop = node.recursive or 'loop' in \ + find_undeclared(node.iter_child_nodes( + only=('body',)), ('loop',)) + + # if we don't have an recursive loop we have to find the shadowed + # variables at that point. Because loops can be nested but the loop + # variable is a special one we have to enforce aliasing for it. + if not node.recursive: + aliases = self.push_scope(loop_frame, ('loop',)) + + # otherwise we set up a buffer and add a function def + else: + self.writeline('def loop(reciter, loop_render_func, depth=0):', node) + self.indent() + self.buffer(loop_frame) + aliases = {} + + # make sure the loop variable is a special one and raise a template + # assertion error if a loop tries to write to loop + if extended_loop: + self.writeline('l_loop = missing') + loop_frame.identifiers.add_special('loop') + for name in node.find_all(nodes.Name): + if name.ctx == 'store' and name.name == 'loop': + self.fail('Can\'t assign to special loop variable ' + 'in for-loop target', name.lineno) + + self.pull_locals(loop_frame) + if node.else_: + iteration_indicator = self.temporary_identifier() + self.writeline('%s = 1' % iteration_indicator) + + # Create a fake parent loop if the else or test section of a + # loop is accessing the special loop variable and no parent loop + # exists. + if 'loop' not in aliases and 'loop' in find_undeclared( + node.iter_child_nodes(only=('else_', 'test')), ('loop',)): + self.writeline("l_loop = environment.undefined(%r, name='loop')" % + ("'loop' is undefined. the filter section of a loop as well " + "as the else block don't have access to the special 'loop'" + " variable of the current loop. Because there is no parent " + "loop it's undefined. Happened in loop on %s" % + self.position(node))) + + self.writeline('for ', node) + self.visit(node.target, loop_frame) + self.write(extended_loop and ', l_loop in LoopContext(' or ' in ') + + # if we have an extened loop and a node test, we filter in the + # "outer frame". + if extended_loop and node.test is not None: + self.write('(') + self.visit(node.target, loop_frame) + self.write(' for ') + self.visit(node.target, loop_frame) + self.write(' in ') + if node.recursive: + self.write('reciter') + else: + self.visit(node.iter, loop_frame) + self.write(' if (') + test_frame = loop_frame.copy() + self.visit(node.test, test_frame) + self.write('))') + + elif node.recursive: + self.write('reciter') + else: + self.visit(node.iter, loop_frame) + + if node.recursive: + self.write(', loop_render_func, depth):') + else: + self.write(extended_loop and '):' or ':') + + # tests in not extended loops become a continue + if not extended_loop and node.test is not None: + self.indent() + self.writeline('if not ') + self.visit(node.test, loop_frame) + self.write(':') + self.indent() + self.writeline('continue') + self.outdent(2) + + self.indent() + self.blockvisit(node.body, loop_frame) + if node.else_: + self.writeline('%s = 0' % iteration_indicator) + self.outdent() + + if node.else_: + self.writeline('if %s:' % iteration_indicator) + self.indent() + self.blockvisit(node.else_, loop_frame) + self.outdent() + + # reset the aliases if there are any. + if not node.recursive: + self.pop_scope(aliases, loop_frame) + + # if the node was recursive we have to return the buffer contents + # and start the iteration code + if node.recursive: + self.return_buffer_contents(loop_frame) + self.outdent() + self.start_write(frame, node) + self.write('loop(') + self.visit(node.iter, frame) + self.write(', loop)') + self.end_write(frame) + + def visit_If(self, node, frame): + if_frame = frame.soft() + self.writeline('if ', node) + self.visit(node.test, if_frame) + self.write(':') + self.indent() + self.blockvisit(node.body, if_frame) + self.outdent() + if node.else_: + self.writeline('else:') + self.indent() + self.blockvisit(node.else_, if_frame) + self.outdent() + + def visit_Macro(self, node, frame): + macro_frame = self.macro_body(node, frame) + self.newline() + if frame.toplevel: + if not node.name.startswith('_'): + self.write('context.exported_vars.add(%r)' % node.name) + self.writeline('context.vars[%r] = ' % node.name) + self.write('l_%s = ' % node.name) + self.macro_def(node, macro_frame) + frame.assigned_names.add(node.name) + + def visit_CallBlock(self, node, frame): + children = node.iter_child_nodes(exclude=('call',)) + call_frame = self.macro_body(node, frame, children) + self.writeline('caller = ') + self.macro_def(node, call_frame) + self.start_write(frame, node) + self.visit_Call(node.call, call_frame, forward_caller=True) + self.end_write(frame) + + def visit_FilterBlock(self, node, frame): + filter_frame = frame.inner() + filter_frame.inspect(node.iter_child_nodes()) + aliases = self.push_scope(filter_frame) + self.pull_locals(filter_frame) + self.buffer(filter_frame) + self.blockvisit(node.body, filter_frame) + self.start_write(frame, node) + self.visit_Filter(node.filter, filter_frame) + self.end_write(frame) + self.pop_scope(aliases, filter_frame) + + def visit_ExprStmt(self, node, frame): + self.newline(node) + self.visit(node.node, frame) + + def visit_Output(self, node, frame): + # if we have a known extends statement, we don't output anything + # if we are in a require_output_check section + if self.has_known_extends and frame.require_output_check: + return + + if self.environment.finalize: + finalize = lambda x: text_type(self.environment.finalize(x)) + else: + finalize = text_type + + # if we are inside a frame that requires output checking, we do so + outdent_later = False + if frame.require_output_check: + self.writeline('if parent_template is None:') + self.indent() + outdent_later = True + + # try to evaluate as many chunks as possible into a static + # string at compile time. + body = [] + for child in node.nodes: + try: + const = child.as_const(frame.eval_ctx) + except nodes.Impossible: + body.append(child) + continue + # the frame can't be volatile here, becaus otherwise the + # as_const() function would raise an Impossible exception + # at that point. + try: + if frame.eval_ctx.autoescape: + if hasattr(const, '__html__'): + const = const.__html__() + else: + const = escape(const) + const = finalize(const) + except Exception: + # if something goes wrong here we evaluate the node + # at runtime for easier debugging + body.append(child) + continue + if body and isinstance(body[-1], list): + body[-1].append(const) + else: + body.append([const]) + + # if we have less than 3 nodes or a buffer we yield or extend/append + if len(body) < 3 or frame.buffer is not None: + if frame.buffer is not None: + # for one item we append, for more we extend + if len(body) == 1: + self.writeline('%s.append(' % frame.buffer) + else: + self.writeline('%s.extend((' % frame.buffer) + self.indent() + for item in body: + if isinstance(item, list): + val = repr(concat(item)) + if frame.buffer is None: + self.writeline('yield ' + val) + else: + self.writeline(val + ', ') + else: + if frame.buffer is None: + self.writeline('yield ', item) + else: + self.newline(item) + close = 1 + if frame.eval_ctx.volatile: + self.write('(context.eval_ctx.autoescape and' + ' escape or to_string)(') + elif frame.eval_ctx.autoescape: + self.write('escape(') + else: + self.write('to_string(') + if self.environment.finalize is not None: + self.write('environment.finalize(') + close += 1 + self.visit(item, frame) + self.write(')' * close) + if frame.buffer is not None: + self.write(', ') + if frame.buffer is not None: + # close the open parentheses + self.outdent() + self.writeline(len(body) == 1 and ')' or '))') + + # otherwise we create a format string as this is faster in that case + else: + format = [] + arguments = [] + for item in body: + if isinstance(item, list): + format.append(concat(item).replace('%', '%%')) + else: + format.append('%s') + arguments.append(item) + self.writeline('yield ') + self.write(repr(concat(format)) + ' % (') + idx = -1 + self.indent() + for argument in arguments: + self.newline(argument) + close = 0 + if frame.eval_ctx.volatile: + self.write('(context.eval_ctx.autoescape and' + ' escape or to_string)(') + close += 1 + elif frame.eval_ctx.autoescape: + self.write('escape(') + close += 1 + if self.environment.finalize is not None: + self.write('environment.finalize(') + close += 1 + self.visit(argument, frame) + self.write(')' * close + ', ') + self.outdent() + self.writeline(')') + + if outdent_later: + self.outdent() + + def visit_Assign(self, node, frame): + self.newline(node) + # toplevel assignments however go into the local namespace and + # the current template's context. We create a copy of the frame + # here and add a set so that the Name visitor can add the assigned + # names here. + if frame.toplevel: + assignment_frame = frame.copy() + assignment_frame.toplevel_assignments = set() + else: + assignment_frame = frame + self.visit(node.target, assignment_frame) + self.write(' = ') + self.visit(node.node, frame) + + # make sure toplevel assignments are added to the context. + if frame.toplevel: + public_names = [x for x in assignment_frame.toplevel_assignments + if not x.startswith('_')] + if len(assignment_frame.toplevel_assignments) == 1: + name = next(iter(assignment_frame.toplevel_assignments)) + self.writeline('context.vars[%r] = l_%s' % (name, name)) + else: + self.writeline('context.vars.update({') + for idx, name in enumerate(assignment_frame.toplevel_assignments): + if idx: + self.write(', ') + self.write('%r: l_%s' % (name, name)) + self.write('})') + if public_names: + if len(public_names) == 1: + self.writeline('context.exported_vars.add(%r)' % + public_names[0]) + else: + self.writeline('context.exported_vars.update((%s))' % + ', '.join(imap(repr, public_names))) + + # -- Expression Visitors + + def visit_Name(self, node, frame): + if node.ctx == 'store' and frame.toplevel: + frame.toplevel_assignments.add(node.name) + self.write('l_' + node.name) + frame.assigned_names.add(node.name) + + def visit_Const(self, node, frame): + val = node.value + if isinstance(val, float): + self.write(str(val)) + else: + self.write(repr(val)) + + def visit_TemplateData(self, node, frame): + try: + self.write(repr(node.as_const(frame.eval_ctx))) + except nodes.Impossible: + self.write('(context.eval_ctx.autoescape and Markup or identity)(%r)' + % node.data) + + def visit_Tuple(self, node, frame): + self.write('(') + idx = -1 + for idx, item in enumerate(node.items): + if idx: + self.write(', ') + self.visit(item, frame) + self.write(idx == 0 and ',)' or ')') + + def visit_List(self, node, frame): + self.write('[') + for idx, item in enumerate(node.items): + if idx: + self.write(', ') + self.visit(item, frame) + self.write(']') + + def visit_Dict(self, node, frame): + self.write('{') + for idx, item in enumerate(node.items): + if idx: + self.write(', ') + self.visit(item.key, frame) + self.write(': ') + self.visit(item.value, frame) + self.write('}') + + def binop(operator, interceptable=True): + def visitor(self, node, frame): + if self.environment.sandboxed and \ + operator in self.environment.intercepted_binops: + self.write('environment.call_binop(context, %r, ' % operator) + self.visit(node.left, frame) + self.write(', ') + self.visit(node.right, frame) + else: + self.write('(') + self.visit(node.left, frame) + self.write(' %s ' % operator) + self.visit(node.right, frame) + self.write(')') + return visitor + + def uaop(operator, interceptable=True): + def visitor(self, node, frame): + if self.environment.sandboxed and \ + operator in self.environment.intercepted_unops: + self.write('environment.call_unop(context, %r, ' % operator) + self.visit(node.node, frame) + else: + self.write('(' + operator) + self.visit(node.node, frame) + self.write(')') + return visitor + + visit_Add = binop('+') + visit_Sub = binop('-') + visit_Mul = binop('*') + visit_Div = binop('/') + visit_FloorDiv = binop('//') + visit_Pow = binop('**') + visit_Mod = binop('%') + visit_And = binop('and', interceptable=False) + visit_Or = binop('or', interceptable=False) + visit_Pos = uaop('+') + visit_Neg = uaop('-') + visit_Not = uaop('not ', interceptable=False) + del binop, uaop + + def visit_Concat(self, node, frame): + if frame.eval_ctx.volatile: + func_name = '(context.eval_ctx.volatile and' \ + ' markup_join or unicode_join)' + elif frame.eval_ctx.autoescape: + func_name = 'markup_join' + else: + func_name = 'unicode_join' + self.write('%s((' % func_name) + for arg in node.nodes: + self.visit(arg, frame) + self.write(', ') + self.write('))') + + def visit_Compare(self, node, frame): + self.visit(node.expr, frame) + for op in node.ops: + self.visit(op, frame) + + def visit_Operand(self, node, frame): + self.write(' %s ' % operators[node.op]) + self.visit(node.expr, frame) + + def visit_Getattr(self, node, frame): + self.write('environment.getattr(') + self.visit(node.node, frame) + self.write(', %r)' % node.attr) + + def visit_Getitem(self, node, frame): + # slices bypass the environment getitem method. + if isinstance(node.arg, nodes.Slice): + self.visit(node.node, frame) + self.write('[') + self.visit(node.arg, frame) + self.write(']') + else: + self.write('environment.getitem(') + self.visit(node.node, frame) + self.write(', ') + self.visit(node.arg, frame) + self.write(')') + + def visit_Slice(self, node, frame): + if node.start is not None: + self.visit(node.start, frame) + self.write(':') + if node.stop is not None: + self.visit(node.stop, frame) + if node.step is not None: + self.write(':') + self.visit(node.step, frame) + + def visit_Filter(self, node, frame): + self.write(self.filters[node.name] + '(') + func = self.environment.filters.get(node.name) + if func is None: + self.fail('no filter named %r' % node.name, node.lineno) + if getattr(func, 'contextfilter', False): + self.write('context, ') + elif getattr(func, 'evalcontextfilter', False): + self.write('context.eval_ctx, ') + elif getattr(func, 'environmentfilter', False): + self.write('environment, ') + + # if the filter node is None we are inside a filter block + # and want to write to the current buffer + if node.node is not None: + self.visit(node.node, frame) + elif frame.eval_ctx.volatile: + self.write('(context.eval_ctx.autoescape and' + ' Markup(concat(%s)) or concat(%s))' % + (frame.buffer, frame.buffer)) + elif frame.eval_ctx.autoescape: + self.write('Markup(concat(%s))' % frame.buffer) + else: + self.write('concat(%s)' % frame.buffer) + self.signature(node, frame) + self.write(')') + + def visit_Test(self, node, frame): + self.write(self.tests[node.name] + '(') + if node.name not in self.environment.tests: + self.fail('no test named %r' % node.name, node.lineno) + self.visit(node.node, frame) + self.signature(node, frame) + self.write(')') + + def visit_CondExpr(self, node, frame): + def write_expr2(): + if node.expr2 is not None: + return self.visit(node.expr2, frame) + self.write('environment.undefined(%r)' % ('the inline if-' + 'expression on %s evaluated to false and ' + 'no else section was defined.' % self.position(node))) + + self.write('(') + self.visit(node.expr1, frame) + self.write(' if ') + self.visit(node.test, frame) + self.write(' else ') + write_expr2() + self.write(')') + + def visit_Call(self, node, frame, forward_caller=False): + if self.environment.sandboxed: + self.write('environment.call(context, ') + else: + self.write('context.call(') + self.visit(node.node, frame) + extra_kwargs = forward_caller and {'caller': 'caller'} or None + self.signature(node, frame, extra_kwargs) + self.write(')') + + def visit_Keyword(self, node, frame): + self.write(node.key + '=') + self.visit(node.value, frame) + + # -- Unused nodes for extensions + + def visit_MarkSafe(self, node, frame): + self.write('Markup(') + self.visit(node.expr, frame) + self.write(')') + + def visit_MarkSafeIfAutoescape(self, node, frame): + self.write('(context.eval_ctx.autoescape and Markup or identity)(') + self.visit(node.expr, frame) + self.write(')') + + def visit_EnvironmentAttribute(self, node, frame): + self.write('environment.' + node.name) + + def visit_ExtensionAttribute(self, node, frame): + self.write('environment.extensions[%r].%s' % (node.identifier, node.name)) + + def visit_ImportedName(self, node, frame): + self.write(self.import_aliases[node.importname]) + + def visit_InternalName(self, node, frame): + self.write(node.name) + + def visit_ContextReference(self, node, frame): + self.write('context') + + def visit_Continue(self, node, frame): + self.writeline('continue', node) + + def visit_Break(self, node, frame): + self.writeline('break', node) + + def visit_Scope(self, node, frame): + scope_frame = frame.inner() + scope_frame.inspect(node.iter_child_nodes()) + aliases = self.push_scope(scope_frame) + self.pull_locals(scope_frame) + self.blockvisit(node.body, scope_frame) + self.pop_scope(aliases, scope_frame) + + def visit_EvalContextModifier(self, node, frame): + for keyword in node.options: + self.writeline('context.eval_ctx.%s = ' % keyword.key) + self.visit(keyword.value, frame) + try: + val = keyword.value.as_const(frame.eval_ctx) + except nodes.Impossible: + frame.eval_ctx.volatile = True + else: + setattr(frame.eval_ctx, keyword.key, val) + + def visit_ScopedEvalContextModifier(self, node, frame): + old_ctx_name = self.temporary_identifier() + safed_ctx = frame.eval_ctx.save() + self.writeline('%s = context.eval_ctx.save()' % old_ctx_name) + self.visit_EvalContextModifier(node, frame) + for child in node.body: + self.visit(child, frame) + frame.eval_ctx.revert(safed_ctx) + self.writeline('context.eval_ctx.revert(%s)' % old_ctx_name) diff --git a/mojo/public/third_party/jinja2/constants.py b/mojo/public/third_party/jinja2/constants.py new file mode 100644 index 0000000..cab203c --- /dev/null +++ b/mojo/public/third_party/jinja2/constants.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +""" + jinja.constants + ~~~~~~~~~~~~~~~ + + Various constants. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" + + +#: list of lorem ipsum words used by the lipsum() helper function +LOREM_IPSUM_WORDS = u'''\ +a ac accumsan ad adipiscing aenean aliquam aliquet amet ante aptent arcu at +auctor augue bibendum blandit class commodo condimentum congue consectetuer +consequat conubia convallis cras cubilia cum curabitur curae cursus dapibus +diam dictum dictumst dignissim dis dolor donec dui duis egestas eget eleifend +elementum elit enim erat eros est et etiam eu euismod facilisi facilisis fames +faucibus felis fermentum feugiat fringilla fusce gravida habitant habitasse hac +hendrerit hymenaeos iaculis id imperdiet in inceptos integer interdum ipsum +justo lacinia lacus laoreet lectus leo libero ligula litora lobortis lorem +luctus maecenas magna magnis malesuada massa mattis mauris metus mi molestie +mollis montes morbi mus nam nascetur natoque nec neque netus nibh nisi nisl non +nonummy nostra nulla nullam nunc odio orci ornare parturient pede pellentesque +penatibus per pharetra phasellus placerat platea porta porttitor posuere +potenti praesent pretium primis proin pulvinar purus quam quis quisque rhoncus +ridiculus risus rutrum sagittis sapien scelerisque sed sem semper senectus sit +sociis sociosqu sodales sollicitudin suscipit suspendisse taciti tellus tempor +tempus tincidunt torquent tortor tristique turpis ullamcorper ultrices +ultricies urna ut varius vehicula vel velit venenatis vestibulum vitae vivamus +viverra volutpat vulputate''' diff --git a/mojo/public/third_party/jinja2/debug.py b/mojo/public/third_party/jinja2/debug.py new file mode 100644 index 0000000..815cc18 --- /dev/null +++ b/mojo/public/third_party/jinja2/debug.py @@ -0,0 +1,337 @@ +# -*- coding: utf-8 -*- +""" + jinja2.debug + ~~~~~~~~~~~~ + + Implements the debug interface for Jinja. This module does some pretty + ugly stuff with the Python traceback system in order to achieve tracebacks + with correct line numbers, locals and contents. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import sys +import traceback +from types import TracebackType +from jinja2.utils import missing, internal_code +from jinja2.exceptions import TemplateSyntaxError +from jinja2._compat import iteritems, reraise, code_type + +# on pypy we can take advantage of transparent proxies +try: + from __pypy__ import tproxy +except ImportError: + tproxy = None + + +# how does the raise helper look like? +try: + exec("raise TypeError, 'foo'") +except SyntaxError: + raise_helper = 'raise __jinja_exception__[1]' +except TypeError: + raise_helper = 'raise __jinja_exception__[0], __jinja_exception__[1]' + + +class TracebackFrameProxy(object): + """Proxies a traceback frame.""" + + def __init__(self, tb): + self.tb = tb + self._tb_next = None + + @property + def tb_next(self): + return self._tb_next + + def set_next(self, next): + if tb_set_next is not None: + try: + tb_set_next(self.tb, next and next.tb or None) + except Exception: + # this function can fail due to all the hackery it does + # on various python implementations. We just catch errors + # down and ignore them if necessary. + pass + self._tb_next = next + + @property + def is_jinja_frame(self): + return '__jinja_template__' in self.tb.tb_frame.f_globals + + def __getattr__(self, name): + return getattr(self.tb, name) + + +def make_frame_proxy(frame): + proxy = TracebackFrameProxy(frame) + if tproxy is None: + return proxy + def operation_handler(operation, *args, **kwargs): + if operation in ('__getattribute__', '__getattr__'): + return getattr(proxy, args[0]) + elif operation == '__setattr__': + proxy.__setattr__(*args, **kwargs) + else: + return getattr(proxy, operation)(*args, **kwargs) + return tproxy(TracebackType, operation_handler) + + +class ProcessedTraceback(object): + """Holds a Jinja preprocessed traceback for printing or reraising.""" + + def __init__(self, exc_type, exc_value, frames): + assert frames, 'no frames for this traceback?' + self.exc_type = exc_type + self.exc_value = exc_value + self.frames = frames + + # newly concatenate the frames (which are proxies) + prev_tb = None + for tb in self.frames: + if prev_tb is not None: + prev_tb.set_next(tb) + prev_tb = tb + prev_tb.set_next(None) + + def render_as_text(self, limit=None): + """Return a string with the traceback.""" + lines = traceback.format_exception(self.exc_type, self.exc_value, + self.frames[0], limit=limit) + return ''.join(lines).rstrip() + + def render_as_html(self, full=False): + """Return a unicode string with the traceback as rendered HTML.""" + from jinja2.debugrenderer import render_traceback + return u'%s\n\n<!--\n%s\n-->' % ( + render_traceback(self, full=full), + self.render_as_text().decode('utf-8', 'replace') + ) + + @property + def is_template_syntax_error(self): + """`True` if this is a template syntax error.""" + return isinstance(self.exc_value, TemplateSyntaxError) + + @property + def exc_info(self): + """Exception info tuple with a proxy around the frame objects.""" + return self.exc_type, self.exc_value, self.frames[0] + + @property + def standard_exc_info(self): + """Standard python exc_info for re-raising""" + tb = self.frames[0] + # the frame will be an actual traceback (or transparent proxy) if + # we are on pypy or a python implementation with support for tproxy + if type(tb) is not TracebackType: + tb = tb.tb + return self.exc_type, self.exc_value, tb + + +def make_traceback(exc_info, source_hint=None): + """Creates a processed traceback object from the exc_info.""" + exc_type, exc_value, tb = exc_info + if isinstance(exc_value, TemplateSyntaxError): + exc_info = translate_syntax_error(exc_value, source_hint) + initial_skip = 0 + else: + initial_skip = 1 + return translate_exception(exc_info, initial_skip) + + +def translate_syntax_error(error, source=None): + """Rewrites a syntax error to please traceback systems.""" + error.source = source + error.translated = True + exc_info = (error.__class__, error, None) + filename = error.filename + if filename is None: + filename = '<unknown>' + return fake_exc_info(exc_info, filename, error.lineno) + + +def translate_exception(exc_info, initial_skip=0): + """If passed an exc_info it will automatically rewrite the exceptions + all the way down to the correct line numbers and frames. + """ + tb = exc_info[2] + frames = [] + + # skip some internal frames if wanted + for x in range(initial_skip): + if tb is not None: + tb = tb.tb_next + initial_tb = tb + + while tb is not None: + # skip frames decorated with @internalcode. These are internal + # calls we can't avoid and that are useless in template debugging + # output. + if tb.tb_frame.f_code in internal_code: + tb = tb.tb_next + continue + + # save a reference to the next frame if we override the current + # one with a faked one. + next = tb.tb_next + + # fake template exceptions + template = tb.tb_frame.f_globals.get('__jinja_template__') + if template is not None: + lineno = template.get_corresponding_lineno(tb.tb_lineno) + tb = fake_exc_info(exc_info[:2] + (tb,), template.filename, + lineno)[2] + + frames.append(make_frame_proxy(tb)) + tb = next + + # if we don't have any exceptions in the frames left, we have to + # reraise it unchanged. + # XXX: can we backup here? when could this happen? + if not frames: + reraise(exc_info[0], exc_info[1], exc_info[2]) + + return ProcessedTraceback(exc_info[0], exc_info[1], frames) + + +def fake_exc_info(exc_info, filename, lineno): + """Helper for `translate_exception`.""" + exc_type, exc_value, tb = exc_info + + # figure the real context out + if tb is not None: + real_locals = tb.tb_frame.f_locals.copy() + ctx = real_locals.get('context') + if ctx: + locals = ctx.get_all() + else: + locals = {} + for name, value in iteritems(real_locals): + if name.startswith('l_') and value is not missing: + locals[name[2:]] = value + + # if there is a local called __jinja_exception__, we get + # rid of it to not break the debug functionality. + locals.pop('__jinja_exception__', None) + else: + locals = {} + + # assamble fake globals we need + globals = { + '__name__': filename, + '__file__': filename, + '__jinja_exception__': exc_info[:2], + + # we don't want to keep the reference to the template around + # to not cause circular dependencies, but we mark it as Jinja + # frame for the ProcessedTraceback + '__jinja_template__': None + } + + # and fake the exception + code = compile('\n' * (lineno - 1) + raise_helper, filename, 'exec') + + # if it's possible, change the name of the code. This won't work + # on some python environments such as google appengine + try: + if tb is None: + location = 'template' + else: + function = tb.tb_frame.f_code.co_name + if function == 'root': + location = 'top-level template code' + elif function.startswith('block_'): + location = 'block "%s"' % function[6:] + else: + location = 'template' + code = code_type(0, code.co_nlocals, code.co_stacksize, + code.co_flags, code.co_code, code.co_consts, + code.co_names, code.co_varnames, filename, + location, code.co_firstlineno, + code.co_lnotab, (), ()) + except: + pass + + # execute the code and catch the new traceback + try: + exec(code, globals, locals) + except: + exc_info = sys.exc_info() + new_tb = exc_info[2].tb_next + + # return without this frame + return exc_info[:2] + (new_tb,) + + +def _init_ugly_crap(): + """This function implements a few ugly things so that we can patch the + traceback objects. The function returned allows resetting `tb_next` on + any python traceback object. Do not attempt to use this on non cpython + interpreters + """ + import ctypes + from types import TracebackType + + # figure out side of _Py_ssize_t + if hasattr(ctypes.pythonapi, 'Py_InitModule4_64'): + _Py_ssize_t = ctypes.c_int64 + else: + _Py_ssize_t = ctypes.c_int + + # regular python + class _PyObject(ctypes.Structure): + pass + _PyObject._fields_ = [ + ('ob_refcnt', _Py_ssize_t), + ('ob_type', ctypes.POINTER(_PyObject)) + ] + + # python with trace + if hasattr(sys, 'getobjects'): + class _PyObject(ctypes.Structure): + pass + _PyObject._fields_ = [ + ('_ob_next', ctypes.POINTER(_PyObject)), + ('_ob_prev', ctypes.POINTER(_PyObject)), + ('ob_refcnt', _Py_ssize_t), + ('ob_type', ctypes.POINTER(_PyObject)) + ] + + class _Traceback(_PyObject): + pass + _Traceback._fields_ = [ + ('tb_next', ctypes.POINTER(_Traceback)), + ('tb_frame', ctypes.POINTER(_PyObject)), + ('tb_lasti', ctypes.c_int), + ('tb_lineno', ctypes.c_int) + ] + + def tb_set_next(tb, next): + """Set the tb_next attribute of a traceback object.""" + if not (isinstance(tb, TracebackType) and + (next is None or isinstance(next, TracebackType))): + raise TypeError('tb_set_next arguments must be traceback objects') + obj = _Traceback.from_address(id(tb)) + if tb.tb_next is not None: + old = _Traceback.from_address(id(tb.tb_next)) + old.ob_refcnt -= 1 + if next is None: + obj.tb_next = ctypes.POINTER(_Traceback)() + else: + next = _Traceback.from_address(id(next)) + next.ob_refcnt += 1 + obj.tb_next = ctypes.pointer(next) + + return tb_set_next + + +# try to get a tb_set_next implementation if we don't have transparent +# proxies. +tb_set_next = None +if tproxy is None: + try: + tb_set_next = _init_ugly_crap() + except: + pass + del _init_ugly_crap diff --git a/mojo/public/third_party/jinja2/defaults.py b/mojo/public/third_party/jinja2/defaults.py new file mode 100644 index 0000000..a27cb80 --- /dev/null +++ b/mojo/public/third_party/jinja2/defaults.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +""" + jinja2.defaults + ~~~~~~~~~~~~~~~ + + Jinja default filters and tags. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +from jinja2._compat import range_type +from jinja2.utils import generate_lorem_ipsum, Cycler, Joiner + + +# defaults for the parser / lexer +BLOCK_START_STRING = '{%' +BLOCK_END_STRING = '%}' +VARIABLE_START_STRING = '{{' +VARIABLE_END_STRING = '}}' +COMMENT_START_STRING = '{#' +COMMENT_END_STRING = '#}' +LINE_STATEMENT_PREFIX = None +LINE_COMMENT_PREFIX = None +TRIM_BLOCKS = False +LSTRIP_BLOCKS = False +NEWLINE_SEQUENCE = '\n' +KEEP_TRAILING_NEWLINE = False + + +# default filters, tests and namespace +from jinja2.filters import FILTERS as DEFAULT_FILTERS +from jinja2.tests import TESTS as DEFAULT_TESTS +DEFAULT_NAMESPACE = { + 'range': range_type, + 'dict': lambda **kw: kw, + 'lipsum': generate_lorem_ipsum, + 'cycler': Cycler, + 'joiner': Joiner +} + + +# export all constants +__all__ = tuple(x for x in locals().keys() if x.isupper()) diff --git a/mojo/public/third_party/jinja2/environment.py b/mojo/public/third_party/jinja2/environment.py new file mode 100644 index 0000000..45fabad --- /dev/null +++ b/mojo/public/third_party/jinja2/environment.py @@ -0,0 +1,1191 @@ +# -*- coding: utf-8 -*- +""" + jinja2.environment + ~~~~~~~~~~~~~~~~~~ + + Provides a class that holds runtime and parsing time options. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import os +import sys +from jinja2 import nodes +from jinja2.defaults import BLOCK_START_STRING, \ + BLOCK_END_STRING, VARIABLE_START_STRING, VARIABLE_END_STRING, \ + COMMENT_START_STRING, COMMENT_END_STRING, LINE_STATEMENT_PREFIX, \ + LINE_COMMENT_PREFIX, TRIM_BLOCKS, NEWLINE_SEQUENCE, \ + DEFAULT_FILTERS, DEFAULT_TESTS, DEFAULT_NAMESPACE, \ + KEEP_TRAILING_NEWLINE, LSTRIP_BLOCKS +from jinja2.lexer import get_lexer, TokenStream +from jinja2.parser import Parser +from jinja2.nodes import EvalContext +from jinja2.optimizer import optimize +from jinja2.compiler import generate +from jinja2.runtime import Undefined, new_context +from jinja2.exceptions import TemplateSyntaxError, TemplateNotFound, \ + TemplatesNotFound, TemplateRuntimeError +from jinja2.utils import import_string, LRUCache, Markup, missing, \ + concat, consume, internalcode +from jinja2._compat import imap, ifilter, string_types, iteritems, \ + text_type, reraise, implements_iterator, implements_to_string, \ + get_next, encode_filename, PY2, PYPY +from functools import reduce + + +# for direct template usage we have up to ten living environments +_spontaneous_environments = LRUCache(10) + +# the function to create jinja traceback objects. This is dynamically +# imported on the first exception in the exception handler. +_make_traceback = None + + +def get_spontaneous_environment(*args): + """Return a new spontaneous environment. A spontaneous environment is an + unnamed and unaccessible (in theory) environment that is used for + templates generated from a string and not from the file system. + """ + try: + env = _spontaneous_environments.get(args) + except TypeError: + return Environment(*args) + if env is not None: + return env + _spontaneous_environments[args] = env = Environment(*args) + env.shared = True + return env + + +def create_cache(size): + """Return the cache class for the given size.""" + if size == 0: + return None + if size < 0: + return {} + return LRUCache(size) + + +def copy_cache(cache): + """Create an empty copy of the given cache.""" + if cache is None: + return None + elif type(cache) is dict: + return {} + return LRUCache(cache.capacity) + + +def load_extensions(environment, extensions): + """Load the extensions from the list and bind it to the environment. + Returns a dict of instantiated environments. + """ + result = {} + for extension in extensions: + if isinstance(extension, string_types): + extension = import_string(extension) + result[extension.identifier] = extension(environment) + return result + + +def _environment_sanity_check(environment): + """Perform a sanity check on the environment.""" + assert issubclass(environment.undefined, Undefined), 'undefined must ' \ + 'be a subclass of undefined because filters depend on it.' + assert environment.block_start_string != \ + environment.variable_start_string != \ + environment.comment_start_string, 'block, variable and comment ' \ + 'start strings must be different' + assert environment.newline_sequence in ('\r', '\r\n', '\n'), \ + 'newline_sequence set to unknown line ending string.' + return environment + + +class Environment(object): + r"""The core component of Jinja is the `Environment`. It contains + important shared variables like configuration, filters, tests, + globals and others. Instances of this class may be modified if + they are not shared and if no template was loaded so far. + Modifications on environments after the first template was loaded + will lead to surprising effects and undefined behavior. + + Here the possible initialization parameters: + + `block_start_string` + The string marking the begin of a block. Defaults to ``'{%'``. + + `block_end_string` + The string marking the end of a block. Defaults to ``'%}'``. + + `variable_start_string` + The string marking the begin of a print statement. + Defaults to ``'{{'``. + + `variable_end_string` + The string marking the end of a print statement. Defaults to + ``'}}'``. + + `comment_start_string` + The string marking the begin of a comment. Defaults to ``'{#'``. + + `comment_end_string` + The string marking the end of a comment. Defaults to ``'#}'``. + + `line_statement_prefix` + If given and a string, this will be used as prefix for line based + statements. See also :ref:`line-statements`. + + `line_comment_prefix` + If given and a string, this will be used as prefix for line based + based comments. See also :ref:`line-statements`. + + .. versionadded:: 2.2 + + `trim_blocks` + If this is set to ``True`` the first newline after a block is + removed (block, not variable tag!). Defaults to `False`. + + `lstrip_blocks` + If this is set to ``True`` leading spaces and tabs are stripped + from the start of a line to a block. Defaults to `False`. + + `newline_sequence` + The sequence that starts a newline. Must be one of ``'\r'``, + ``'\n'`` or ``'\r\n'``. The default is ``'\n'`` which is a + useful default for Linux and OS X systems as well as web + applications. + + `keep_trailing_newline` + Preserve the trailing newline when rendering templates. + The default is ``False``, which causes a single newline, + if present, to be stripped from the end of the template. + + .. versionadded:: 2.7 + + `extensions` + List of Jinja extensions to use. This can either be import paths + as strings or extension classes. For more information have a + look at :ref:`the extensions documentation <jinja-extensions>`. + + `optimized` + should the optimizer be enabled? Default is `True`. + + `undefined` + :class:`Undefined` or a subclass of it that is used to represent + undefined values in the template. + + `finalize` + A callable that can be used to process the result of a variable + expression before it is output. For example one can convert + `None` implicitly into an empty string here. + + `autoescape` + If set to true the XML/HTML autoescaping feature is enabled by + default. For more details about auto escaping see + :class:`~jinja2.utils.Markup`. As of Jinja 2.4 this can also + be a callable that is passed the template name and has to + return `True` or `False` depending on autoescape should be + enabled by default. + + .. versionchanged:: 2.4 + `autoescape` can now be a function + + `loader` + The template loader for this environment. + + `cache_size` + The size of the cache. Per default this is ``50`` which means + that if more than 50 templates are loaded the loader will clean + out the least recently used template. If the cache size is set to + ``0`` templates are recompiled all the time, if the cache size is + ``-1`` the cache will not be cleaned. + + `auto_reload` + Some loaders load templates from locations where the template + sources may change (ie: file system or database). If + `auto_reload` is set to `True` (default) every time a template is + requested the loader checks if the source changed and if yes, it + will reload the template. For higher performance it's possible to + disable that. + + `bytecode_cache` + If set to a bytecode cache object, this object will provide a + cache for the internal Jinja bytecode so that templates don't + have to be parsed if they were not changed. + + See :ref:`bytecode-cache` for more information. + """ + + #: if this environment is sandboxed. Modifying this variable won't make + #: the environment sandboxed though. For a real sandboxed environment + #: have a look at jinja2.sandbox. This flag alone controls the code + #: generation by the compiler. + sandboxed = False + + #: True if the environment is just an overlay + overlayed = False + + #: the environment this environment is linked to if it is an overlay + linked_to = None + + #: shared environments have this set to `True`. A shared environment + #: must not be modified + shared = False + + #: these are currently EXPERIMENTAL undocumented features. + exception_handler = None + exception_formatter = None + + def __init__(self, + block_start_string=BLOCK_START_STRING, + block_end_string=BLOCK_END_STRING, + variable_start_string=VARIABLE_START_STRING, + variable_end_string=VARIABLE_END_STRING, + comment_start_string=COMMENT_START_STRING, + comment_end_string=COMMENT_END_STRING, + line_statement_prefix=LINE_STATEMENT_PREFIX, + line_comment_prefix=LINE_COMMENT_PREFIX, + trim_blocks=TRIM_BLOCKS, + lstrip_blocks=LSTRIP_BLOCKS, + newline_sequence=NEWLINE_SEQUENCE, + keep_trailing_newline=KEEP_TRAILING_NEWLINE, + extensions=(), + optimized=True, + undefined=Undefined, + finalize=None, + autoescape=False, + loader=None, + cache_size=50, + auto_reload=True, + bytecode_cache=None): + # !!Important notice!! + # The constructor accepts quite a few arguments that should be + # passed by keyword rather than position. However it's important to + # not change the order of arguments because it's used at least + # internally in those cases: + # - spontaneous environments (i18n extension and Template) + # - unittests + # If parameter changes are required only add parameters at the end + # and don't change the arguments (or the defaults!) of the arguments + # existing already. + + # lexer / parser information + self.block_start_string = block_start_string + self.block_end_string = block_end_string + self.variable_start_string = variable_start_string + self.variable_end_string = variable_end_string + self.comment_start_string = comment_start_string + self.comment_end_string = comment_end_string + self.line_statement_prefix = line_statement_prefix + self.line_comment_prefix = line_comment_prefix + self.trim_blocks = trim_blocks + self.lstrip_blocks = lstrip_blocks + self.newline_sequence = newline_sequence + self.keep_trailing_newline = keep_trailing_newline + + # runtime information + self.undefined = undefined + self.optimized = optimized + self.finalize = finalize + self.autoescape = autoescape + + # defaults + self.filters = DEFAULT_FILTERS.copy() + self.tests = DEFAULT_TESTS.copy() + self.globals = DEFAULT_NAMESPACE.copy() + + # set the loader provided + self.loader = loader + self.cache = create_cache(cache_size) + self.bytecode_cache = bytecode_cache + self.auto_reload = auto_reload + + # load extensions + self.extensions = load_extensions(self, extensions) + + _environment_sanity_check(self) + + def add_extension(self, extension): + """Adds an extension after the environment was created. + + .. versionadded:: 2.5 + """ + self.extensions.update(load_extensions(self, [extension])) + + def extend(self, **attributes): + """Add the items to the instance of the environment if they do not exist + yet. This is used by :ref:`extensions <writing-extensions>` to register + callbacks and configuration values without breaking inheritance. + """ + for key, value in iteritems(attributes): + if not hasattr(self, key): + setattr(self, key, value) + + def overlay(self, block_start_string=missing, block_end_string=missing, + variable_start_string=missing, variable_end_string=missing, + comment_start_string=missing, comment_end_string=missing, + line_statement_prefix=missing, line_comment_prefix=missing, + trim_blocks=missing, lstrip_blocks=missing, + extensions=missing, optimized=missing, + undefined=missing, finalize=missing, autoescape=missing, + loader=missing, cache_size=missing, auto_reload=missing, + bytecode_cache=missing): + """Create a new overlay environment that shares all the data with the + current environment except of cache and the overridden attributes. + Extensions cannot be removed for an overlayed environment. An overlayed + environment automatically gets all the extensions of the environment it + is linked to plus optional extra extensions. + + Creating overlays should happen after the initial environment was set + up completely. Not all attributes are truly linked, some are just + copied over so modifications on the original environment may not shine + through. + """ + args = dict(locals()) + del args['self'], args['cache_size'], args['extensions'] + + rv = object.__new__(self.__class__) + rv.__dict__.update(self.__dict__) + rv.overlayed = True + rv.linked_to = self + + for key, value in iteritems(args): + if value is not missing: + setattr(rv, key, value) + + if cache_size is not missing: + rv.cache = create_cache(cache_size) + else: + rv.cache = copy_cache(self.cache) + + rv.extensions = {} + for key, value in iteritems(self.extensions): + rv.extensions[key] = value.bind(rv) + if extensions is not missing: + rv.extensions.update(load_extensions(rv, extensions)) + + return _environment_sanity_check(rv) + + lexer = property(get_lexer, doc="The lexer for this environment.") + + def iter_extensions(self): + """Iterates over the extensions by priority.""" + return iter(sorted(self.extensions.values(), + key=lambda x: x.priority)) + + def getitem(self, obj, argument): + """Get an item or attribute of an object but prefer the item.""" + try: + return obj[argument] + except (TypeError, LookupError): + if isinstance(argument, string_types): + try: + attr = str(argument) + except Exception: + pass + else: + try: + return getattr(obj, attr) + except AttributeError: + pass + return self.undefined(obj=obj, name=argument) + + def getattr(self, obj, attribute): + """Get an item or attribute of an object but prefer the attribute. + Unlike :meth:`getitem` the attribute *must* be a bytestring. + """ + try: + return getattr(obj, attribute) + except AttributeError: + pass + try: + return obj[attribute] + except (TypeError, LookupError, AttributeError): + return self.undefined(obj=obj, name=attribute) + + def call_filter(self, name, value, args=None, kwargs=None, + context=None, eval_ctx=None): + """Invokes a filter on a value the same way the compiler does it. + + .. versionadded:: 2.7 + """ + func = self.filters.get(name) + if func is None: + raise TemplateRuntimeError('no filter named %r' % name) + args = [value] + list(args or ()) + if getattr(func, 'contextfilter', False): + if context is None: + raise TemplateRuntimeError('Attempted to invoke context ' + 'filter without context') + args.insert(0, context) + elif getattr(func, 'evalcontextfilter', False): + if eval_ctx is None: + if context is not None: + eval_ctx = context.eval_ctx + else: + eval_ctx = EvalContext(self) + args.insert(0, eval_ctx) + elif getattr(func, 'environmentfilter', False): + args.insert(0, self) + return func(*args, **(kwargs or {})) + + def call_test(self, name, value, args=None, kwargs=None): + """Invokes a test on a value the same way the compiler does it. + + .. versionadded:: 2.7 + """ + func = self.tests.get(name) + if func is None: + raise TemplateRuntimeError('no test named %r' % name) + return func(value, *(args or ()), **(kwargs or {})) + + @internalcode + def parse(self, source, name=None, filename=None): + """Parse the sourcecode and return the abstract syntax tree. This + tree of nodes is used by the compiler to convert the template into + executable source- or bytecode. This is useful for debugging or to + extract information from templates. + + If you are :ref:`developing Jinja2 extensions <writing-extensions>` + this gives you a good overview of the node tree generated. + """ + try: + return self._parse(source, name, filename) + except TemplateSyntaxError: + exc_info = sys.exc_info() + self.handle_exception(exc_info, source_hint=source) + + def _parse(self, source, name, filename): + """Internal parsing function used by `parse` and `compile`.""" + return Parser(self, source, name, encode_filename(filename)).parse() + + def lex(self, source, name=None, filename=None): + """Lex the given sourcecode and return a generator that yields + tokens as tuples in the form ``(lineno, token_type, value)``. + This can be useful for :ref:`extension development <writing-extensions>` + and debugging templates. + + This does not perform preprocessing. If you want the preprocessing + of the extensions to be applied you have to filter source through + the :meth:`preprocess` method. + """ + source = text_type(source) + try: + return self.lexer.tokeniter(source, name, filename) + except TemplateSyntaxError: + exc_info = sys.exc_info() + self.handle_exception(exc_info, source_hint=source) + + def preprocess(self, source, name=None, filename=None): + """Preprocesses the source with all extensions. This is automatically + called for all parsing and compiling methods but *not* for :meth:`lex` + because there you usually only want the actual source tokenized. + """ + return reduce(lambda s, e: e.preprocess(s, name, filename), + self.iter_extensions(), text_type(source)) + + def _tokenize(self, source, name, filename=None, state=None): + """Called by the parser to do the preprocessing and filtering + for all the extensions. Returns a :class:`~jinja2.lexer.TokenStream`. + """ + source = self.preprocess(source, name, filename) + stream = self.lexer.tokenize(source, name, filename, state) + for ext in self.iter_extensions(): + stream = ext.filter_stream(stream) + if not isinstance(stream, TokenStream): + stream = TokenStream(stream, name, filename) + return stream + + def _generate(self, source, name, filename, defer_init=False): + """Internal hook that can be overridden to hook a different generate + method in. + + .. versionadded:: 2.5 + """ + return generate(source, self, name, filename, defer_init=defer_init) + + def _compile(self, source, filename): + """Internal hook that can be overridden to hook a different compile + method in. + + .. versionadded:: 2.5 + """ + return compile(source, filename, 'exec') + + @internalcode + def compile(self, source, name=None, filename=None, raw=False, + defer_init=False): + """Compile a node or template source code. The `name` parameter is + the load name of the template after it was joined using + :meth:`join_path` if necessary, not the filename on the file system. + the `filename` parameter is the estimated filename of the template on + the file system. If the template came from a database or memory this + can be omitted. + + The return value of this method is a python code object. If the `raw` + parameter is `True` the return value will be a string with python + code equivalent to the bytecode returned otherwise. This method is + mainly used internally. + + `defer_init` is use internally to aid the module code generator. This + causes the generated code to be able to import without the global + environment variable to be set. + + .. versionadded:: 2.4 + `defer_init` parameter added. + """ + source_hint = None + try: + if isinstance(source, string_types): + source_hint = source + source = self._parse(source, name, filename) + if self.optimized: + source = optimize(source, self) + source = self._generate(source, name, filename, + defer_init=defer_init) + if raw: + return source + if filename is None: + filename = '<template>' + else: + filename = encode_filename(filename) + return self._compile(source, filename) + except TemplateSyntaxError: + exc_info = sys.exc_info() + self.handle_exception(exc_info, source_hint=source) + + def compile_expression(self, source, undefined_to_none=True): + """A handy helper method that returns a callable that accepts keyword + arguments that appear as variables in the expression. If called it + returns the result of the expression. + + This is useful if applications want to use the same rules as Jinja + in template "configuration files" or similar situations. + + Example usage: + + >>> env = Environment() + >>> expr = env.compile_expression('foo == 42') + >>> expr(foo=23) + False + >>> expr(foo=42) + True + + Per default the return value is converted to `None` if the + expression returns an undefined value. This can be changed + by setting `undefined_to_none` to `False`. + + >>> env.compile_expression('var')() is None + True + >>> env.compile_expression('var', undefined_to_none=False)() + Undefined + + .. versionadded:: 2.1 + """ + parser = Parser(self, source, state='variable') + exc_info = None + try: + expr = parser.parse_expression() + if not parser.stream.eos: + raise TemplateSyntaxError('chunk after expression', + parser.stream.current.lineno, + None, None) + expr.set_environment(self) + except TemplateSyntaxError: + exc_info = sys.exc_info() + if exc_info is not None: + self.handle_exception(exc_info, source_hint=source) + body = [nodes.Assign(nodes.Name('result', 'store'), expr, lineno=1)] + template = self.from_string(nodes.Template(body, lineno=1)) + return TemplateExpression(template, undefined_to_none) + + def compile_templates(self, target, extensions=None, filter_func=None, + zip='deflated', log_function=None, + ignore_errors=True, py_compile=False): + """Finds all the templates the loader can find, compiles them + and stores them in `target`. If `zip` is `None`, instead of in a + zipfile, the templates will be will be stored in a directory. + By default a deflate zip algorithm is used, to switch to + the stored algorithm, `zip` can be set to ``'stored'``. + + `extensions` and `filter_func` are passed to :meth:`list_templates`. + Each template returned will be compiled to the target folder or + zipfile. + + By default template compilation errors are ignored. In case a + log function is provided, errors are logged. If you want template + syntax errors to abort the compilation you can set `ignore_errors` + to `False` and you will get an exception on syntax errors. + + If `py_compile` is set to `True` .pyc files will be written to the + target instead of standard .py files. This flag does not do anything + on pypy and Python 3 where pyc files are not picked up by itself and + don't give much benefit. + + .. versionadded:: 2.4 + """ + from jinja2.loaders import ModuleLoader + + if log_function is None: + log_function = lambda x: None + + if py_compile: + if not PY2 or PYPY: + from warnings import warn + warn(Warning('py_compile has no effect on pypy or Python 3')) + py_compile = False + else: + import imp, marshal + py_header = imp.get_magic() + \ + u'\xff\xff\xff\xff'.encode('iso-8859-15') + + # Python 3.3 added a source filesize to the header + if sys.version_info >= (3, 3): + py_header += u'\x00\x00\x00\x00'.encode('iso-8859-15') + + def write_file(filename, data, mode): + if zip: + info = ZipInfo(filename) + info.external_attr = 0o755 << 16 + zip_file.writestr(info, data) + else: + f = open(os.path.join(target, filename), mode) + try: + f.write(data) + finally: + f.close() + + if zip is not None: + from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED, ZIP_STORED + zip_file = ZipFile(target, 'w', dict(deflated=ZIP_DEFLATED, + stored=ZIP_STORED)[zip]) + log_function('Compiling into Zip archive "%s"' % target) + else: + if not os.path.isdir(target): + os.makedirs(target) + log_function('Compiling into folder "%s"' % target) + + try: + for name in self.list_templates(extensions, filter_func): + source, filename, _ = self.loader.get_source(self, name) + try: + code = self.compile(source, name, filename, True, True) + except TemplateSyntaxError as e: + if not ignore_errors: + raise + log_function('Could not compile "%s": %s' % (name, e)) + continue + + filename = ModuleLoader.get_module_filename(name) + + if py_compile: + c = self._compile(code, encode_filename(filename)) + write_file(filename + 'c', py_header + + marshal.dumps(c), 'wb') + log_function('Byte-compiled "%s" as %s' % + (name, filename + 'c')) + else: + write_file(filename, code, 'w') + log_function('Compiled "%s" as %s' % (name, filename)) + finally: + if zip: + zip_file.close() + + log_function('Finished compiling templates') + + def list_templates(self, extensions=None, filter_func=None): + """Returns a list of templates for this environment. This requires + that the loader supports the loader's + :meth:`~BaseLoader.list_templates` method. + + If there are other files in the template folder besides the + actual templates, the returned list can be filtered. There are two + ways: either `extensions` is set to a list of file extensions for + templates, or a `filter_func` can be provided which is a callable that + is passed a template name and should return `True` if it should end up + in the result list. + + If the loader does not support that, a :exc:`TypeError` is raised. + + .. versionadded:: 2.4 + """ + x = self.loader.list_templates() + if extensions is not None: + if filter_func is not None: + raise TypeError('either extensions or filter_func ' + 'can be passed, but not both') + filter_func = lambda x: '.' in x and \ + x.rsplit('.', 1)[1] in extensions + if filter_func is not None: + x = ifilter(filter_func, x) + return x + + def handle_exception(self, exc_info=None, rendered=False, source_hint=None): + """Exception handling helper. This is used internally to either raise + rewritten exceptions or return a rendered traceback for the template. + """ + global _make_traceback + if exc_info is None: + exc_info = sys.exc_info() + + # the debugging module is imported when it's used for the first time. + # we're doing a lot of stuff there and for applications that do not + # get any exceptions in template rendering there is no need to load + # all of that. + if _make_traceback is None: + from jinja2.debug import make_traceback as _make_traceback + traceback = _make_traceback(exc_info, source_hint) + if rendered and self.exception_formatter is not None: + return self.exception_formatter(traceback) + if self.exception_handler is not None: + self.exception_handler(traceback) + exc_type, exc_value, tb = traceback.standard_exc_info + reraise(exc_type, exc_value, tb) + + def join_path(self, template, parent): + """Join a template with the parent. By default all the lookups are + relative to the loader root so this method returns the `template` + parameter unchanged, but if the paths should be relative to the + parent template, this function can be used to calculate the real + template name. + + Subclasses may override this method and implement template path + joining here. + """ + return template + + @internalcode + def _load_template(self, name, globals): + if self.loader is None: + raise TypeError('no loader for this environment specified') + if self.cache is not None: + template = self.cache.get(name) + if template is not None and (not self.auto_reload or \ + template.is_up_to_date): + return template + template = self.loader.load(self, name, globals) + if self.cache is not None: + self.cache[name] = template + return template + + @internalcode + def get_template(self, name, parent=None, globals=None): + """Load a template from the loader. If a loader is configured this + method ask the loader for the template and returns a :class:`Template`. + If the `parent` parameter is not `None`, :meth:`join_path` is called + to get the real template name before loading. + + The `globals` parameter can be used to provide template wide globals. + These variables are available in the context at render time. + + If the template does not exist a :exc:`TemplateNotFound` exception is + raised. + + .. versionchanged:: 2.4 + If `name` is a :class:`Template` object it is returned from the + function unchanged. + """ + if isinstance(name, Template): + return name + if parent is not None: + name = self.join_path(name, parent) + return self._load_template(name, self.make_globals(globals)) + + @internalcode + def select_template(self, names, parent=None, globals=None): + """Works like :meth:`get_template` but tries a number of templates + before it fails. If it cannot find any of the templates, it will + raise a :exc:`TemplatesNotFound` exception. + + .. versionadded:: 2.3 + + .. versionchanged:: 2.4 + If `names` contains a :class:`Template` object it is returned + from the function unchanged. + """ + if not names: + raise TemplatesNotFound(message=u'Tried to select from an empty list ' + u'of templates.') + globals = self.make_globals(globals) + for name in names: + if isinstance(name, Template): + return name + if parent is not None: + name = self.join_path(name, parent) + try: + return self._load_template(name, globals) + except TemplateNotFound: + pass + raise TemplatesNotFound(names) + + @internalcode + def get_or_select_template(self, template_name_or_list, + parent=None, globals=None): + """Does a typecheck and dispatches to :meth:`select_template` + if an iterable of template names is given, otherwise to + :meth:`get_template`. + + .. versionadded:: 2.3 + """ + if isinstance(template_name_or_list, string_types): + return self.get_template(template_name_or_list, parent, globals) + elif isinstance(template_name_or_list, Template): + return template_name_or_list + return self.select_template(template_name_or_list, parent, globals) + + def from_string(self, source, globals=None, template_class=None): + """Load a template from a string. This parses the source given and + returns a :class:`Template` object. + """ + globals = self.make_globals(globals) + cls = template_class or self.template_class + return cls.from_code(self, self.compile(source), globals, None) + + def make_globals(self, d): + """Return a dict for the globals.""" + if not d: + return self.globals + return dict(self.globals, **d) + + +class Template(object): + """The central template object. This class represents a compiled template + and is used to evaluate it. + + Normally the template object is generated from an :class:`Environment` but + it also has a constructor that makes it possible to create a template + instance directly using the constructor. It takes the same arguments as + the environment constructor but it's not possible to specify a loader. + + Every template object has a few methods and members that are guaranteed + to exist. However it's important that a template object should be + considered immutable. Modifications on the object are not supported. + + Template objects created from the constructor rather than an environment + do have an `environment` attribute that points to a temporary environment + that is probably shared with other templates created with the constructor + and compatible settings. + + >>> template = Template('Hello {{ name }}!') + >>> template.render(name='John Doe') + u'Hello John Doe!' + + >>> stream = template.stream(name='John Doe') + >>> stream.next() + u'Hello John Doe!' + >>> stream.next() + Traceback (most recent call last): + ... + StopIteration + """ + + def __new__(cls, source, + block_start_string=BLOCK_START_STRING, + block_end_string=BLOCK_END_STRING, + variable_start_string=VARIABLE_START_STRING, + variable_end_string=VARIABLE_END_STRING, + comment_start_string=COMMENT_START_STRING, + comment_end_string=COMMENT_END_STRING, + line_statement_prefix=LINE_STATEMENT_PREFIX, + line_comment_prefix=LINE_COMMENT_PREFIX, + trim_blocks=TRIM_BLOCKS, + lstrip_blocks=LSTRIP_BLOCKS, + newline_sequence=NEWLINE_SEQUENCE, + keep_trailing_newline=KEEP_TRAILING_NEWLINE, + extensions=(), + optimized=True, + undefined=Undefined, + finalize=None, + autoescape=False): + env = get_spontaneous_environment( + block_start_string, block_end_string, variable_start_string, + variable_end_string, comment_start_string, comment_end_string, + line_statement_prefix, line_comment_prefix, trim_blocks, + lstrip_blocks, newline_sequence, keep_trailing_newline, + frozenset(extensions), optimized, undefined, finalize, autoescape, + None, 0, False, None) + return env.from_string(source, template_class=cls) + + @classmethod + def from_code(cls, environment, code, globals, uptodate=None): + """Creates a template object from compiled code and the globals. This + is used by the loaders and environment to create a template object. + """ + namespace = { + 'environment': environment, + '__file__': code.co_filename + } + exec(code, namespace) + rv = cls._from_namespace(environment, namespace, globals) + rv._uptodate = uptodate + return rv + + @classmethod + def from_module_dict(cls, environment, module_dict, globals): + """Creates a template object from a module. This is used by the + module loader to create a template object. + + .. versionadded:: 2.4 + """ + return cls._from_namespace(environment, module_dict, globals) + + @classmethod + def _from_namespace(cls, environment, namespace, globals): + t = object.__new__(cls) + t.environment = environment + t.globals = globals + t.name = namespace['name'] + t.filename = namespace['__file__'] + t.blocks = namespace['blocks'] + + # render function and module + t.root_render_func = namespace['root'] + t._module = None + + # debug and loader helpers + t._debug_info = namespace['debug_info'] + t._uptodate = None + + # store the reference + namespace['environment'] = environment + namespace['__jinja_template__'] = t + + return t + + def render(self, *args, **kwargs): + """This method accepts the same arguments as the `dict` constructor: + A dict, a dict subclass or some keyword arguments. If no arguments + are given the context will be empty. These two calls do the same:: + + template.render(knights='that say nih') + template.render({'knights': 'that say nih'}) + + This will return the rendered template as unicode string. + """ + vars = dict(*args, **kwargs) + try: + return concat(self.root_render_func(self.new_context(vars))) + except Exception: + exc_info = sys.exc_info() + return self.environment.handle_exception(exc_info, True) + + def stream(self, *args, **kwargs): + """Works exactly like :meth:`generate` but returns a + :class:`TemplateStream`. + """ + return TemplateStream(self.generate(*args, **kwargs)) + + def generate(self, *args, **kwargs): + """For very large templates it can be useful to not render the whole + template at once but evaluate each statement after another and yield + piece for piece. This method basically does exactly that and returns + a generator that yields one item after another as unicode strings. + + It accepts the same arguments as :meth:`render`. + """ + vars = dict(*args, **kwargs) + try: + for event in self.root_render_func(self.new_context(vars)): + yield event + except Exception: + exc_info = sys.exc_info() + else: + return + yield self.environment.handle_exception(exc_info, True) + + def new_context(self, vars=None, shared=False, locals=None): + """Create a new :class:`Context` for this template. The vars + provided will be passed to the template. Per default the globals + are added to the context. If shared is set to `True` the data + is passed as it to the context without adding the globals. + + `locals` can be a dict of local variables for internal usage. + """ + return new_context(self.environment, self.name, self.blocks, + vars, shared, self.globals, locals) + + def make_module(self, vars=None, shared=False, locals=None): + """This method works like the :attr:`module` attribute when called + without arguments but it will evaluate the template on every call + rather than caching it. It's also possible to provide + a dict which is then used as context. The arguments are the same + as for the :meth:`new_context` method. + """ + return TemplateModule(self, self.new_context(vars, shared, locals)) + + @property + def module(self): + """The template as module. This is used for imports in the + template runtime but is also useful if one wants to access + exported template variables from the Python layer: + + >>> t = Template('{% macro foo() %}42{% endmacro %}23') + >>> unicode(t.module) + u'23' + >>> t.module.foo() + u'42' + """ + if self._module is not None: + return self._module + self._module = rv = self.make_module() + return rv + + def get_corresponding_lineno(self, lineno): + """Return the source line number of a line number in the + generated bytecode as they are not in sync. + """ + for template_line, code_line in reversed(self.debug_info): + if code_line <= lineno: + return template_line + return 1 + + @property + def is_up_to_date(self): + """If this variable is `False` there is a newer version available.""" + if self._uptodate is None: + return True + return self._uptodate() + + @property + def debug_info(self): + """The debug info mapping.""" + return [tuple(imap(int, x.split('='))) for x in + self._debug_info.split('&')] + + def __repr__(self): + if self.name is None: + name = 'memory:%x' % id(self) + else: + name = repr(self.name) + return '<%s %s>' % (self.__class__.__name__, name) + + +@implements_to_string +class TemplateModule(object): + """Represents an imported template. All the exported names of the + template are available as attributes on this object. Additionally + converting it into an unicode- or bytestrings renders the contents. + """ + + def __init__(self, template, context): + self._body_stream = list(template.root_render_func(context)) + self.__dict__.update(context.get_exported()) + self.__name__ = template.name + + def __html__(self): + return Markup(concat(self._body_stream)) + + def __str__(self): + return concat(self._body_stream) + + def __repr__(self): + if self.__name__ is None: + name = 'memory:%x' % id(self) + else: + name = repr(self.__name__) + return '<%s %s>' % (self.__class__.__name__, name) + + +class TemplateExpression(object): + """The :meth:`jinja2.Environment.compile_expression` method returns an + instance of this object. It encapsulates the expression-like access + to the template with an expression it wraps. + """ + + def __init__(self, template, undefined_to_none): + self._template = template + self._undefined_to_none = undefined_to_none + + def __call__(self, *args, **kwargs): + context = self._template.new_context(dict(*args, **kwargs)) + consume(self._template.root_render_func(context)) + rv = context.vars['result'] + if self._undefined_to_none and isinstance(rv, Undefined): + rv = None + return rv + + +@implements_iterator +class TemplateStream(object): + """A template stream works pretty much like an ordinary python generator + but it can buffer multiple items to reduce the number of total iterations. + Per default the output is unbuffered which means that for every unbuffered + instruction in the template one unicode string is yielded. + + If buffering is enabled with a buffer size of 5, five items are combined + into a new unicode string. This is mainly useful if you are streaming + big templates to a client via WSGI which flushes after each iteration. + """ + + def __init__(self, gen): + self._gen = gen + self.disable_buffering() + + def dump(self, fp, encoding=None, errors='strict'): + """Dump the complete stream into a file or file-like object. + Per default unicode strings are written, if you want to encode + before writing specify an `encoding`. + + Example usage:: + + Template('Hello {{ name }}!').stream(name='foo').dump('hello.html') + """ + close = False + if isinstance(fp, string_types): + fp = open(fp, encoding is None and 'w' or 'wb') + close = True + try: + if encoding is not None: + iterable = (x.encode(encoding, errors) for x in self) + else: + iterable = self + if hasattr(fp, 'writelines'): + fp.writelines(iterable) + else: + for item in iterable: + fp.write(item) + finally: + if close: + fp.close() + + def disable_buffering(self): + """Disable the output buffering.""" + self._next = get_next(self._gen) + self.buffered = False + + def enable_buffering(self, size=5): + """Enable buffering. Buffer `size` items before yielding them.""" + if size <= 1: + raise ValueError('buffer size too small') + + def generator(next): + buf = [] + c_size = 0 + push = buf.append + + while 1: + try: + while c_size < size: + c = next() + push(c) + if c: + c_size += 1 + except StopIteration: + if not c_size: + return + yield concat(buf) + del buf[:] + c_size = 0 + + self.buffered = True + self._next = get_next(generator(get_next(self._gen))) + + def __iter__(self): + return self + + def __next__(self): + return self._next() + + +# hook in default template class. if anyone reads this comment: ignore that +# it's possible to use custom templates ;-) +Environment.template_class = Template diff --git a/mojo/public/third_party/jinja2/exceptions.py b/mojo/public/third_party/jinja2/exceptions.py new file mode 100644 index 0000000..c9df6dc --- /dev/null +++ b/mojo/public/third_party/jinja2/exceptions.py @@ -0,0 +1,146 @@ +# -*- coding: utf-8 -*- +""" + jinja2.exceptions + ~~~~~~~~~~~~~~~~~ + + Jinja exceptions. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +from jinja2._compat import imap, text_type, PY2, implements_to_string + + +class TemplateError(Exception): + """Baseclass for all template errors.""" + + if PY2: + def __init__(self, message=None): + if message is not None: + message = text_type(message).encode('utf-8') + Exception.__init__(self, message) + + @property + def message(self): + if self.args: + message = self.args[0] + if message is not None: + return message.decode('utf-8', 'replace') + + def __unicode__(self): + return self.message or u'' + else: + def __init__(self, message=None): + Exception.__init__(self, message) + + @property + def message(self): + if self.args: + message = self.args[0] + if message is not None: + return message + + +@implements_to_string +class TemplateNotFound(IOError, LookupError, TemplateError): + """Raised if a template does not exist.""" + + # looks weird, but removes the warning descriptor that just + # bogusly warns us about message being deprecated + message = None + + def __init__(self, name, message=None): + IOError.__init__(self) + if message is None: + message = name + self.message = message + self.name = name + self.templates = [name] + + def __str__(self): + return self.message + + +class TemplatesNotFound(TemplateNotFound): + """Like :class:`TemplateNotFound` but raised if multiple templates + are selected. This is a subclass of :class:`TemplateNotFound` + exception, so just catching the base exception will catch both. + + .. versionadded:: 2.2 + """ + + def __init__(self, names=(), message=None): + if message is None: + message = u'none of the templates given were found: ' + \ + u', '.join(imap(text_type, names)) + TemplateNotFound.__init__(self, names and names[-1] or None, message) + self.templates = list(names) + + +@implements_to_string +class TemplateSyntaxError(TemplateError): + """Raised to tell the user that there is a problem with the template.""" + + def __init__(self, message, lineno, name=None, filename=None): + TemplateError.__init__(self, message) + self.lineno = lineno + self.name = name + self.filename = filename + self.source = None + + # this is set to True if the debug.translate_syntax_error + # function translated the syntax error into a new traceback + self.translated = False + + def __str__(self): + # for translated errors we only return the message + if self.translated: + return self.message + + # otherwise attach some stuff + location = 'line %d' % self.lineno + name = self.filename or self.name + if name: + location = 'File "%s", %s' % (name, location) + lines = [self.message, ' ' + location] + + # if the source is set, add the line to the output + if self.source is not None: + try: + line = self.source.splitlines()[self.lineno - 1] + except IndexError: + line = None + if line: + lines.append(' ' + line.strip()) + + return u'\n'.join(lines) + + +class TemplateAssertionError(TemplateSyntaxError): + """Like a template syntax error, but covers cases where something in the + template caused an error at compile time that wasn't necessarily caused + by a syntax error. However it's a direct subclass of + :exc:`TemplateSyntaxError` and has the same attributes. + """ + + +class TemplateRuntimeError(TemplateError): + """A generic runtime error in the template engine. Under some situations + Jinja may raise this exception. + """ + + +class UndefinedError(TemplateRuntimeError): + """Raised if a template tries to operate on :class:`Undefined`.""" + + +class SecurityError(TemplateRuntimeError): + """Raised if a template tries to do something insecure if the + sandbox is enabled. + """ + + +class FilterArgumentError(TemplateRuntimeError): + """This error is raised if a filter was called with inappropriate + arguments + """ diff --git a/mojo/public/third_party/jinja2/ext.py b/mojo/public/third_party/jinja2/ext.py new file mode 100644 index 0000000..c2df12d --- /dev/null +++ b/mojo/public/third_party/jinja2/ext.py @@ -0,0 +1,636 @@ +# -*- coding: utf-8 -*- +""" + jinja2.ext + ~~~~~~~~~~ + + Jinja extensions allow to add custom tags similar to the way django custom + tags work. By default two example extensions exist: an i18n and a cache + extension. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD. +""" +from jinja2 import nodes +from jinja2.defaults import BLOCK_START_STRING, \ + BLOCK_END_STRING, VARIABLE_START_STRING, VARIABLE_END_STRING, \ + COMMENT_START_STRING, COMMENT_END_STRING, LINE_STATEMENT_PREFIX, \ + LINE_COMMENT_PREFIX, TRIM_BLOCKS, NEWLINE_SEQUENCE, \ + KEEP_TRAILING_NEWLINE, LSTRIP_BLOCKS +from jinja2.environment import Environment +from jinja2.runtime import concat +from jinja2.exceptions import TemplateAssertionError, TemplateSyntaxError +from jinja2.utils import contextfunction, import_string, Markup +from jinja2._compat import next, with_metaclass, string_types, iteritems + + +# the only real useful gettext functions for a Jinja template. Note +# that ugettext must be assigned to gettext as Jinja doesn't support +# non unicode strings. +GETTEXT_FUNCTIONS = ('_', 'gettext', 'ngettext') + + +class ExtensionRegistry(type): + """Gives the extension an unique identifier.""" + + def __new__(cls, name, bases, d): + rv = type.__new__(cls, name, bases, d) + rv.identifier = rv.__module__ + '.' + rv.__name__ + return rv + + +class Extension(with_metaclass(ExtensionRegistry, object)): + """Extensions can be used to add extra functionality to the Jinja template + system at the parser level. Custom extensions are bound to an environment + but may not store environment specific data on `self`. The reason for + this is that an extension can be bound to another environment (for + overlays) by creating a copy and reassigning the `environment` attribute. + + As extensions are created by the environment they cannot accept any + arguments for configuration. One may want to work around that by using + a factory function, but that is not possible as extensions are identified + by their import name. The correct way to configure the extension is + storing the configuration values on the environment. Because this way the + environment ends up acting as central configuration storage the + attributes may clash which is why extensions have to ensure that the names + they choose for configuration are not too generic. ``prefix`` for example + is a terrible name, ``fragment_cache_prefix`` on the other hand is a good + name as includes the name of the extension (fragment cache). + """ + + #: if this extension parses this is the list of tags it's listening to. + tags = set() + + #: the priority of that extension. This is especially useful for + #: extensions that preprocess values. A lower value means higher + #: priority. + #: + #: .. versionadded:: 2.4 + priority = 100 + + def __init__(self, environment): + self.environment = environment + + def bind(self, environment): + """Create a copy of this extension bound to another environment.""" + rv = object.__new__(self.__class__) + rv.__dict__.update(self.__dict__) + rv.environment = environment + return rv + + def preprocess(self, source, name, filename=None): + """This method is called before the actual lexing and can be used to + preprocess the source. The `filename` is optional. The return value + must be the preprocessed source. + """ + return source + + def filter_stream(self, stream): + """It's passed a :class:`~jinja2.lexer.TokenStream` that can be used + to filter tokens returned. This method has to return an iterable of + :class:`~jinja2.lexer.Token`\s, but it doesn't have to return a + :class:`~jinja2.lexer.TokenStream`. + + In the `ext` folder of the Jinja2 source distribution there is a file + called `inlinegettext.py` which implements a filter that utilizes this + method. + """ + return stream + + def parse(self, parser): + """If any of the :attr:`tags` matched this method is called with the + parser as first argument. The token the parser stream is pointing at + is the name token that matched. This method has to return one or a + list of multiple nodes. + """ + raise NotImplementedError() + + def attr(self, name, lineno=None): + """Return an attribute node for the current extension. This is useful + to pass constants on extensions to generated template code. + + :: + + self.attr('_my_attribute', lineno=lineno) + """ + return nodes.ExtensionAttribute(self.identifier, name, lineno=lineno) + + def call_method(self, name, args=None, kwargs=None, dyn_args=None, + dyn_kwargs=None, lineno=None): + """Call a method of the extension. This is a shortcut for + :meth:`attr` + :class:`jinja2.nodes.Call`. + """ + if args is None: + args = [] + if kwargs is None: + kwargs = [] + return nodes.Call(self.attr(name, lineno=lineno), args, kwargs, + dyn_args, dyn_kwargs, lineno=lineno) + + +@contextfunction +def _gettext_alias(__context, *args, **kwargs): + return __context.call(__context.resolve('gettext'), *args, **kwargs) + + +def _make_new_gettext(func): + @contextfunction + def gettext(__context, __string, **variables): + rv = __context.call(func, __string) + if __context.eval_ctx.autoescape: + rv = Markup(rv) + return rv % variables + return gettext + + +def _make_new_ngettext(func): + @contextfunction + def ngettext(__context, __singular, __plural, __num, **variables): + variables.setdefault('num', __num) + rv = __context.call(func, __singular, __plural, __num) + if __context.eval_ctx.autoescape: + rv = Markup(rv) + return rv % variables + return ngettext + + +class InternationalizationExtension(Extension): + """This extension adds gettext support to Jinja2.""" + tags = set(['trans']) + + # TODO: the i18n extension is currently reevaluating values in a few + # situations. Take this example: + # {% trans count=something() %}{{ count }} foo{% pluralize + # %}{{ count }} fooss{% endtrans %} + # something is called twice here. One time for the gettext value and + # the other time for the n-parameter of the ngettext function. + + def __init__(self, environment): + Extension.__init__(self, environment) + environment.globals['_'] = _gettext_alias + environment.extend( + install_gettext_translations=self._install, + install_null_translations=self._install_null, + install_gettext_callables=self._install_callables, + uninstall_gettext_translations=self._uninstall, + extract_translations=self._extract, + newstyle_gettext=False + ) + + def _install(self, translations, newstyle=None): + gettext = getattr(translations, 'ugettext', None) + if gettext is None: + gettext = translations.gettext + ngettext = getattr(translations, 'ungettext', None) + if ngettext is None: + ngettext = translations.ngettext + self._install_callables(gettext, ngettext, newstyle) + + def _install_null(self, newstyle=None): + self._install_callables( + lambda x: x, + lambda s, p, n: (n != 1 and (p,) or (s,))[0], + newstyle + ) + + def _install_callables(self, gettext, ngettext, newstyle=None): + if newstyle is not None: + self.environment.newstyle_gettext = newstyle + if self.environment.newstyle_gettext: + gettext = _make_new_gettext(gettext) + ngettext = _make_new_ngettext(ngettext) + self.environment.globals.update( + gettext=gettext, + ngettext=ngettext + ) + + def _uninstall(self, translations): + for key in 'gettext', 'ngettext': + self.environment.globals.pop(key, None) + + def _extract(self, source, gettext_functions=GETTEXT_FUNCTIONS): + if isinstance(source, string_types): + source = self.environment.parse(source) + return extract_from_ast(source, gettext_functions) + + def parse(self, parser): + """Parse a translatable tag.""" + lineno = next(parser.stream).lineno + num_called_num = False + + # find all the variables referenced. Additionally a variable can be + # defined in the body of the trans block too, but this is checked at + # a later state. + plural_expr = None + plural_expr_assignment = None + variables = {} + while parser.stream.current.type != 'block_end': + if variables: + parser.stream.expect('comma') + + # skip colon for python compatibility + if parser.stream.skip_if('colon'): + break + + name = parser.stream.expect('name') + if name.value in variables: + parser.fail('translatable variable %r defined twice.' % + name.value, name.lineno, + exc=TemplateAssertionError) + + # expressions + if parser.stream.current.type == 'assign': + next(parser.stream) + variables[name.value] = var = parser.parse_expression() + else: + variables[name.value] = var = nodes.Name(name.value, 'load') + + if plural_expr is None: + if isinstance(var, nodes.Call): + plural_expr = nodes.Name('_trans', 'load') + variables[name.value] = plural_expr + plural_expr_assignment = nodes.Assign( + nodes.Name('_trans', 'store'), var) + else: + plural_expr = var + num_called_num = name.value == 'num' + + parser.stream.expect('block_end') + + plural = plural_names = None + have_plural = False + referenced = set() + + # now parse until endtrans or pluralize + singular_names, singular = self._parse_block(parser, True) + if singular_names: + referenced.update(singular_names) + if plural_expr is None: + plural_expr = nodes.Name(singular_names[0], 'load') + num_called_num = singular_names[0] == 'num' + + # if we have a pluralize block, we parse that too + if parser.stream.current.test('name:pluralize'): + have_plural = True + next(parser.stream) + if parser.stream.current.type != 'block_end': + name = parser.stream.expect('name') + if name.value not in variables: + parser.fail('unknown variable %r for pluralization' % + name.value, name.lineno, + exc=TemplateAssertionError) + plural_expr = variables[name.value] + num_called_num = name.value == 'num' + parser.stream.expect('block_end') + plural_names, plural = self._parse_block(parser, False) + next(parser.stream) + referenced.update(plural_names) + else: + next(parser.stream) + + # register free names as simple name expressions + for var in referenced: + if var not in variables: + variables[var] = nodes.Name(var, 'load') + + if not have_plural: + plural_expr = None + elif plural_expr is None: + parser.fail('pluralize without variables', lineno) + + node = self._make_node(singular, plural, variables, plural_expr, + bool(referenced), + num_called_num and have_plural) + node.set_lineno(lineno) + if plural_expr_assignment is not None: + return [plural_expr_assignment, node] + else: + return node + + def _parse_block(self, parser, allow_pluralize): + """Parse until the next block tag with a given name.""" + referenced = [] + buf = [] + while 1: + if parser.stream.current.type == 'data': + buf.append(parser.stream.current.value.replace('%', '%%')) + next(parser.stream) + elif parser.stream.current.type == 'variable_begin': + next(parser.stream) + name = parser.stream.expect('name').value + referenced.append(name) + buf.append('%%(%s)s' % name) + parser.stream.expect('variable_end') + elif parser.stream.current.type == 'block_begin': + next(parser.stream) + if parser.stream.current.test('name:endtrans'): + break + elif parser.stream.current.test('name:pluralize'): + if allow_pluralize: + break + parser.fail('a translatable section can have only one ' + 'pluralize section') + parser.fail('control structures in translatable sections are ' + 'not allowed') + elif parser.stream.eos: + parser.fail('unclosed translation block') + else: + assert False, 'internal parser error' + + return referenced, concat(buf) + + def _make_node(self, singular, plural, variables, plural_expr, + vars_referenced, num_called_num): + """Generates a useful node from the data provided.""" + # no variables referenced? no need to escape for old style + # gettext invocations only if there are vars. + if not vars_referenced and not self.environment.newstyle_gettext: + singular = singular.replace('%%', '%') + if plural: + plural = plural.replace('%%', '%') + + # singular only: + if plural_expr is None: + gettext = nodes.Name('gettext', 'load') + node = nodes.Call(gettext, [nodes.Const(singular)], + [], None, None) + + # singular and plural + else: + ngettext = nodes.Name('ngettext', 'load') + node = nodes.Call(ngettext, [ + nodes.Const(singular), + nodes.Const(plural), + plural_expr + ], [], None, None) + + # in case newstyle gettext is used, the method is powerful + # enough to handle the variable expansion and autoescape + # handling itself + if self.environment.newstyle_gettext: + for key, value in iteritems(variables): + # the function adds that later anyways in case num was + # called num, so just skip it. + if num_called_num and key == 'num': + continue + node.kwargs.append(nodes.Keyword(key, value)) + + # otherwise do that here + else: + # mark the return value as safe if we are in an + # environment with autoescaping turned on + node = nodes.MarkSafeIfAutoescape(node) + if variables: + node = nodes.Mod(node, nodes.Dict([ + nodes.Pair(nodes.Const(key), value) + for key, value in variables.items() + ])) + return nodes.Output([node]) + + +class ExprStmtExtension(Extension): + """Adds a `do` tag to Jinja2 that works like the print statement just + that it doesn't print the return value. + """ + tags = set(['do']) + + def parse(self, parser): + node = nodes.ExprStmt(lineno=next(parser.stream).lineno) + node.node = parser.parse_tuple() + return node + + +class LoopControlExtension(Extension): + """Adds break and continue to the template engine.""" + tags = set(['break', 'continue']) + + def parse(self, parser): + token = next(parser.stream) + if token.value == 'break': + return nodes.Break(lineno=token.lineno) + return nodes.Continue(lineno=token.lineno) + + +class WithExtension(Extension): + """Adds support for a django-like with block.""" + tags = set(['with']) + + def parse(self, parser): + node = nodes.Scope(lineno=next(parser.stream).lineno) + assignments = [] + while parser.stream.current.type != 'block_end': + lineno = parser.stream.current.lineno + if assignments: + parser.stream.expect('comma') + target = parser.parse_assign_target() + parser.stream.expect('assign') + expr = parser.parse_expression() + assignments.append(nodes.Assign(target, expr, lineno=lineno)) + node.body = assignments + \ + list(parser.parse_statements(('name:endwith',), + drop_needle=True)) + return node + + +class AutoEscapeExtension(Extension): + """Changes auto escape rules for a scope.""" + tags = set(['autoescape']) + + def parse(self, parser): + node = nodes.ScopedEvalContextModifier(lineno=next(parser.stream).lineno) + node.options = [ + nodes.Keyword('autoescape', parser.parse_expression()) + ] + node.body = parser.parse_statements(('name:endautoescape',), + drop_needle=True) + return nodes.Scope([node]) + + +def extract_from_ast(node, gettext_functions=GETTEXT_FUNCTIONS, + babel_style=True): + """Extract localizable strings from the given template node. Per + default this function returns matches in babel style that means non string + parameters as well as keyword arguments are returned as `None`. This + allows Babel to figure out what you really meant if you are using + gettext functions that allow keyword arguments for placeholder expansion. + If you don't want that behavior set the `babel_style` parameter to `False` + which causes only strings to be returned and parameters are always stored + in tuples. As a consequence invalid gettext calls (calls without a single + string parameter or string parameters after non-string parameters) are + skipped. + + This example explains the behavior: + + >>> from jinja2 import Environment + >>> env = Environment() + >>> node = env.parse('{{ (_("foo"), _(), ngettext("foo", "bar", 42)) }}') + >>> list(extract_from_ast(node)) + [(1, '_', 'foo'), (1, '_', ()), (1, 'ngettext', ('foo', 'bar', None))] + >>> list(extract_from_ast(node, babel_style=False)) + [(1, '_', ('foo',)), (1, 'ngettext', ('foo', 'bar'))] + + For every string found this function yields a ``(lineno, function, + message)`` tuple, where: + + * ``lineno`` is the number of the line on which the string was found, + * ``function`` is the name of the ``gettext`` function used (if the + string was extracted from embedded Python code), and + * ``message`` is the string itself (a ``unicode`` object, or a tuple + of ``unicode`` objects for functions with multiple string arguments). + + This extraction function operates on the AST and is because of that unable + to extract any comments. For comment support you have to use the babel + extraction interface or extract comments yourself. + """ + for node in node.find_all(nodes.Call): + if not isinstance(node.node, nodes.Name) or \ + node.node.name not in gettext_functions: + continue + + strings = [] + for arg in node.args: + if isinstance(arg, nodes.Const) and \ + isinstance(arg.value, string_types): + strings.append(arg.value) + else: + strings.append(None) + + for arg in node.kwargs: + strings.append(None) + if node.dyn_args is not None: + strings.append(None) + if node.dyn_kwargs is not None: + strings.append(None) + + if not babel_style: + strings = tuple(x for x in strings if x is not None) + if not strings: + continue + else: + if len(strings) == 1: + strings = strings[0] + else: + strings = tuple(strings) + yield node.lineno, node.node.name, strings + + +class _CommentFinder(object): + """Helper class to find comments in a token stream. Can only + find comments for gettext calls forwards. Once the comment + from line 4 is found, a comment for line 1 will not return a + usable value. + """ + + def __init__(self, tokens, comment_tags): + self.tokens = tokens + self.comment_tags = comment_tags + self.offset = 0 + self.last_lineno = 0 + + def find_backwards(self, offset): + try: + for _, token_type, token_value in \ + reversed(self.tokens[self.offset:offset]): + if token_type in ('comment', 'linecomment'): + try: + prefix, comment = token_value.split(None, 1) + except ValueError: + continue + if prefix in self.comment_tags: + return [comment.rstrip()] + return [] + finally: + self.offset = offset + + def find_comments(self, lineno): + if not self.comment_tags or self.last_lineno > lineno: + return [] + for idx, (token_lineno, _, _) in enumerate(self.tokens[self.offset:]): + if token_lineno > lineno: + return self.find_backwards(self.offset + idx) + return self.find_backwards(len(self.tokens)) + + +def babel_extract(fileobj, keywords, comment_tags, options): + """Babel extraction method for Jinja templates. + + .. versionchanged:: 2.3 + Basic support for translation comments was added. If `comment_tags` + is now set to a list of keywords for extraction, the extractor will + try to find the best preceeding comment that begins with one of the + keywords. For best results, make sure to not have more than one + gettext call in one line of code and the matching comment in the + same line or the line before. + + .. versionchanged:: 2.5.1 + The `newstyle_gettext` flag can be set to `True` to enable newstyle + gettext calls. + + .. versionchanged:: 2.7 + A `silent` option can now be provided. If set to `False` template + syntax errors are propagated instead of being ignored. + + :param fileobj: the file-like object the messages should be extracted from + :param keywords: a list of keywords (i.e. function names) that should be + recognized as translation functions + :param comment_tags: a list of translator tags to search for and include + in the results. + :param options: a dictionary of additional options (optional) + :return: an iterator over ``(lineno, funcname, message, comments)`` tuples. + (comments will be empty currently) + """ + extensions = set() + for extension in options.get('extensions', '').split(','): + extension = extension.strip() + if not extension: + continue + extensions.add(import_string(extension)) + if InternationalizationExtension not in extensions: + extensions.add(InternationalizationExtension) + + def getbool(options, key, default=False): + return options.get(key, str(default)).lower() in \ + ('1', 'on', 'yes', 'true') + + silent = getbool(options, 'silent', True) + environment = Environment( + options.get('block_start_string', BLOCK_START_STRING), + options.get('block_end_string', BLOCK_END_STRING), + options.get('variable_start_string', VARIABLE_START_STRING), + options.get('variable_end_string', VARIABLE_END_STRING), + options.get('comment_start_string', COMMENT_START_STRING), + options.get('comment_end_string', COMMENT_END_STRING), + options.get('line_statement_prefix') or LINE_STATEMENT_PREFIX, + options.get('line_comment_prefix') or LINE_COMMENT_PREFIX, + getbool(options, 'trim_blocks', TRIM_BLOCKS), + getbool(options, 'lstrip_blocks', LSTRIP_BLOCKS), + NEWLINE_SEQUENCE, + getbool(options, 'keep_trailing_newline', KEEP_TRAILING_NEWLINE), + frozenset(extensions), + cache_size=0, + auto_reload=False + ) + + if getbool(options, 'newstyle_gettext'): + environment.newstyle_gettext = True + + source = fileobj.read().decode(options.get('encoding', 'utf-8')) + try: + node = environment.parse(source) + tokens = list(environment.lex(environment.preprocess(source))) + except TemplateSyntaxError as e: + if not silent: + raise + # skip templates with syntax errors + return + + finder = _CommentFinder(tokens, comment_tags) + for lineno, func, message in extract_from_ast(node, keywords): + yield lineno, func, message, finder.find_comments(lineno) + + +#: nicer import names +i18n = InternationalizationExtension +do = ExprStmtExtension +loopcontrols = LoopControlExtension +with_ = WithExtension +autoescape = AutoEscapeExtension diff --git a/mojo/public/third_party/jinja2/filters.py b/mojo/public/third_party/jinja2/filters.py new file mode 100644 index 0000000..fd0db04a --- /dev/null +++ b/mojo/public/third_party/jinja2/filters.py @@ -0,0 +1,987 @@ +# -*- coding: utf-8 -*- +""" + jinja2.filters + ~~~~~~~~~~~~~~ + + Bundled jinja filters. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import re +import math + +from random import choice +from operator import itemgetter +from itertools import groupby +from jinja2.utils import Markup, escape, pformat, urlize, soft_unicode, \ + unicode_urlencode +from jinja2.runtime import Undefined +from jinja2.exceptions import FilterArgumentError +from jinja2._compat import next, imap, string_types, text_type, iteritems + + +_word_re = re.compile(r'\w+(?u)') + + +def contextfilter(f): + """Decorator for marking context dependent filters. The current + :class:`Context` will be passed as first argument. + """ + f.contextfilter = True + return f + + +def evalcontextfilter(f): + """Decorator for marking eval-context dependent filters. An eval + context object is passed as first argument. For more information + about the eval context, see :ref:`eval-context`. + + .. versionadded:: 2.4 + """ + f.evalcontextfilter = True + return f + + +def environmentfilter(f): + """Decorator for marking evironment dependent filters. The current + :class:`Environment` is passed to the filter as first argument. + """ + f.environmentfilter = True + return f + + +def make_attrgetter(environment, attribute): + """Returns a callable that looks up the given attribute from a + passed object with the rules of the environment. Dots are allowed + to access attributes of attributes. Integer parts in paths are + looked up as integers. + """ + if not isinstance(attribute, string_types) \ + or ('.' not in attribute and not attribute.isdigit()): + return lambda x: environment.getitem(x, attribute) + attribute = attribute.split('.') + def attrgetter(item): + for part in attribute: + if part.isdigit(): + part = int(part) + item = environment.getitem(item, part) + return item + return attrgetter + + +def do_forceescape(value): + """Enforce HTML escaping. This will probably double escape variables.""" + if hasattr(value, '__html__'): + value = value.__html__() + return escape(text_type(value)) + + +def do_urlencode(value): + """Escape strings for use in URLs (uses UTF-8 encoding). It accepts both + dictionaries and regular strings as well as pairwise iterables. + + .. versionadded:: 2.7 + """ + itemiter = None + if isinstance(value, dict): + itemiter = iteritems(value) + elif not isinstance(value, string_types): + try: + itemiter = iter(value) + except TypeError: + pass + if itemiter is None: + return unicode_urlencode(value) + return u'&'.join(unicode_urlencode(k) + '=' + + unicode_urlencode(v) for k, v in itemiter) + + +@evalcontextfilter +def do_replace(eval_ctx, s, old, new, count=None): + """Return a copy of the value with all occurrences of a substring + replaced with a new one. The first argument is the substring + that should be replaced, the second is the replacement string. + If the optional third argument ``count`` is given, only the first + ``count`` occurrences are replaced: + + .. sourcecode:: jinja + + {{ "Hello World"|replace("Hello", "Goodbye") }} + -> Goodbye World + + {{ "aaaaargh"|replace("a", "d'oh, ", 2) }} + -> d'oh, d'oh, aaargh + """ + if count is None: + count = -1 + if not eval_ctx.autoescape: + return text_type(s).replace(text_type(old), text_type(new), count) + if hasattr(old, '__html__') or hasattr(new, '__html__') and \ + not hasattr(s, '__html__'): + s = escape(s) + else: + s = soft_unicode(s) + return s.replace(soft_unicode(old), soft_unicode(new), count) + + +def do_upper(s): + """Convert a value to uppercase.""" + return soft_unicode(s).upper() + + +def do_lower(s): + """Convert a value to lowercase.""" + return soft_unicode(s).lower() + + +@evalcontextfilter +def do_xmlattr(_eval_ctx, d, autospace=True): + """Create an SGML/XML attribute string based on the items in a dict. + All values that are neither `none` nor `undefined` are automatically + escaped: + + .. sourcecode:: html+jinja + + <ul{{ {'class': 'my_list', 'missing': none, + 'id': 'list-%d'|format(variable)}|xmlattr }}> + ... + </ul> + + Results in something like this: + + .. sourcecode:: html + + <ul class="my_list" id="list-42"> + ... + </ul> + + As you can see it automatically prepends a space in front of the item + if the filter returned something unless the second parameter is false. + """ + rv = u' '.join( + u'%s="%s"' % (escape(key), escape(value)) + for key, value in iteritems(d) + if value is not None and not isinstance(value, Undefined) + ) + if autospace and rv: + rv = u' ' + rv + if _eval_ctx.autoescape: + rv = Markup(rv) + return rv + + +def do_capitalize(s): + """Capitalize a value. The first character will be uppercase, all others + lowercase. + """ + return soft_unicode(s).capitalize() + + +def do_title(s): + """Return a titlecased version of the value. I.e. words will start with + uppercase letters, all remaining characters are lowercase. + """ + rv = [] + for item in re.compile(r'([-\s]+)(?u)').split(s): + if not item: + continue + rv.append(item[0].upper() + item[1:].lower()) + return ''.join(rv) + + +def do_dictsort(value, case_sensitive=False, by='key'): + """Sort a dict and yield (key, value) pairs. Because python dicts are + unsorted you may want to use this function to order them by either + key or value: + + .. sourcecode:: jinja + + {% for item in mydict|dictsort %} + sort the dict by key, case insensitive + + {% for item in mydict|dictsort(true) %} + sort the dict by key, case sensitive + + {% for item in mydict|dictsort(false, 'value') %} + sort the dict by key, case insensitive, sorted + normally and ordered by value. + """ + if by == 'key': + pos = 0 + elif by == 'value': + pos = 1 + else: + raise FilterArgumentError('You can only sort by either ' + '"key" or "value"') + def sort_func(item): + value = item[pos] + if isinstance(value, string_types) and not case_sensitive: + value = value.lower() + return value + + return sorted(value.items(), key=sort_func) + + +@environmentfilter +def do_sort(environment, value, reverse=False, case_sensitive=False, + attribute=None): + """Sort an iterable. Per default it sorts ascending, if you pass it + true as first argument it will reverse the sorting. + + If the iterable is made of strings the third parameter can be used to + control the case sensitiveness of the comparison which is disabled by + default. + + .. sourcecode:: jinja + + {% for item in iterable|sort %} + ... + {% endfor %} + + It is also possible to sort by an attribute (for example to sort + by the date of an object) by specifying the `attribute` parameter: + + .. sourcecode:: jinja + + {% for item in iterable|sort(attribute='date') %} + ... + {% endfor %} + + .. versionchanged:: 2.6 + The `attribute` parameter was added. + """ + if not case_sensitive: + def sort_func(item): + if isinstance(item, string_types): + item = item.lower() + return item + else: + sort_func = None + if attribute is not None: + getter = make_attrgetter(environment, attribute) + def sort_func(item, processor=sort_func or (lambda x: x)): + return processor(getter(item)) + return sorted(value, key=sort_func, reverse=reverse) + + +def do_default(value, default_value=u'', boolean=False): + """If the value is undefined it will return the passed default value, + otherwise the value of the variable: + + .. sourcecode:: jinja + + {{ my_variable|default('my_variable is not defined') }} + + This will output the value of ``my_variable`` if the variable was + defined, otherwise ``'my_variable is not defined'``. If you want + to use default with variables that evaluate to false you have to + set the second parameter to `true`: + + .. sourcecode:: jinja + + {{ ''|default('the string was empty', true) }} + """ + if isinstance(value, Undefined) or (boolean and not value): + return default_value + return value + + +@evalcontextfilter +def do_join(eval_ctx, value, d=u'', attribute=None): + """Return a string which is the concatenation of the strings in the + sequence. The separator between elements is an empty string per + default, you can define it with the optional parameter: + + .. sourcecode:: jinja + + {{ [1, 2, 3]|join('|') }} + -> 1|2|3 + + {{ [1, 2, 3]|join }} + -> 123 + + It is also possible to join certain attributes of an object: + + .. sourcecode:: jinja + + {{ users|join(', ', attribute='username') }} + + .. versionadded:: 2.6 + The `attribute` parameter was added. + """ + if attribute is not None: + value = imap(make_attrgetter(eval_ctx.environment, attribute), value) + + # no automatic escaping? joining is a lot eaiser then + if not eval_ctx.autoescape: + return text_type(d).join(imap(text_type, value)) + + # if the delimiter doesn't have an html representation we check + # if any of the items has. If yes we do a coercion to Markup + if not hasattr(d, '__html__'): + value = list(value) + do_escape = False + for idx, item in enumerate(value): + if hasattr(item, '__html__'): + do_escape = True + else: + value[idx] = text_type(item) + if do_escape: + d = escape(d) + else: + d = text_type(d) + return d.join(value) + + # no html involved, to normal joining + return soft_unicode(d).join(imap(soft_unicode, value)) + + +def do_center(value, width=80): + """Centers the value in a field of a given width.""" + return text_type(value).center(width) + + +@environmentfilter +def do_first(environment, seq): + """Return the first item of a sequence.""" + try: + return next(iter(seq)) + except StopIteration: + return environment.undefined('No first item, sequence was empty.') + + +@environmentfilter +def do_last(environment, seq): + """Return the last item of a sequence.""" + try: + return next(iter(reversed(seq))) + except StopIteration: + return environment.undefined('No last item, sequence was empty.') + + +@environmentfilter +def do_random(environment, seq): + """Return a random item from the sequence.""" + try: + return choice(seq) + except IndexError: + return environment.undefined('No random item, sequence was empty.') + + +def do_filesizeformat(value, binary=False): + """Format the value like a 'human-readable' file size (i.e. 13 kB, + 4.1 MB, 102 Bytes, etc). Per default decimal prefixes are used (Mega, + Giga, etc.), if the second parameter is set to `True` the binary + prefixes are used (Mebi, Gibi). + """ + bytes = float(value) + base = binary and 1024 or 1000 + prefixes = [ + (binary and 'KiB' or 'kB'), + (binary and 'MiB' or 'MB'), + (binary and 'GiB' or 'GB'), + (binary and 'TiB' or 'TB'), + (binary and 'PiB' or 'PB'), + (binary and 'EiB' or 'EB'), + (binary and 'ZiB' or 'ZB'), + (binary and 'YiB' or 'YB') + ] + if bytes == 1: + return '1 Byte' + elif bytes < base: + return '%d Bytes' % bytes + else: + for i, prefix in enumerate(prefixes): + unit = base ** (i + 2) + if bytes < unit: + return '%.1f %s' % ((base * bytes / unit), prefix) + return '%.1f %s' % ((base * bytes / unit), prefix) + + +def do_pprint(value, verbose=False): + """Pretty print a variable. Useful for debugging. + + With Jinja 1.2 onwards you can pass it a parameter. If this parameter + is truthy the output will be more verbose (this requires `pretty`) + """ + return pformat(value, verbose=verbose) + + +@evalcontextfilter +def do_urlize(eval_ctx, value, trim_url_limit=None, nofollow=False): + """Converts URLs in plain text into clickable links. + + If you pass the filter an additional integer it will shorten the urls + to that number. Also a third argument exists that makes the urls + "nofollow": + + .. sourcecode:: jinja + + {{ mytext|urlize(40, true) }} + links are shortened to 40 chars and defined with rel="nofollow" + """ + rv = urlize(value, trim_url_limit, nofollow) + if eval_ctx.autoescape: + rv = Markup(rv) + return rv + + +def do_indent(s, width=4, indentfirst=False): + """Return a copy of the passed string, each line indented by + 4 spaces. The first line is not indented. If you want to + change the number of spaces or indent the first line too + you can pass additional parameters to the filter: + + .. sourcecode:: jinja + + {{ mytext|indent(2, true) }} + indent by two spaces and indent the first line too. + """ + indention = u' ' * width + rv = (u'\n' + indention).join(s.splitlines()) + if indentfirst: + rv = indention + rv + return rv + + +def do_truncate(s, length=255, killwords=False, end='...'): + """Return a truncated copy of the string. The length is specified + with the first parameter which defaults to ``255``. If the second + parameter is ``true`` the filter will cut the text at length. Otherwise + it will discard the last word. If the text was in fact + truncated it will append an ellipsis sign (``"..."``). If you want a + different ellipsis sign than ``"..."`` you can specify it using the + third parameter. + + .. sourcecode:: jinja + + {{ "foo bar"|truncate(5) }} + -> "foo ..." + {{ "foo bar"|truncate(5, True) }} + -> "foo b..." + """ + if len(s) <= length: + return s + elif killwords: + return s[:length] + end + words = s.split(' ') + result = [] + m = 0 + for word in words: + m += len(word) + 1 + if m > length: + break + result.append(word) + result.append(end) + return u' '.join(result) + +@environmentfilter +def do_wordwrap(environment, s, width=79, break_long_words=True, + wrapstring=None): + """ + Return a copy of the string passed to the filter wrapped after + ``79`` characters. You can override this default using the first + parameter. If you set the second parameter to `false` Jinja will not + split words apart if they are longer than `width`. By default, the newlines + will be the default newlines for the environment, but this can be changed + using the wrapstring keyword argument. + + .. versionadded:: 2.7 + Added support for the `wrapstring` parameter. + """ + if not wrapstring: + wrapstring = environment.newline_sequence + import textwrap + return wrapstring.join(textwrap.wrap(s, width=width, expand_tabs=False, + replace_whitespace=False, + break_long_words=break_long_words)) + + +def do_wordcount(s): + """Count the words in that string.""" + return len(_word_re.findall(s)) + + +def do_int(value, default=0): + """Convert the value into an integer. If the + conversion doesn't work it will return ``0``. You can + override this default using the first parameter. + """ + try: + return int(value) + except (TypeError, ValueError): + # this quirk is necessary so that "42.23"|int gives 42. + try: + return int(float(value)) + except (TypeError, ValueError): + return default + + +def do_float(value, default=0.0): + """Convert the value into a floating point number. If the + conversion doesn't work it will return ``0.0``. You can + override this default using the first parameter. + """ + try: + return float(value) + except (TypeError, ValueError): + return default + + +def do_format(value, *args, **kwargs): + """ + Apply python string formatting on an object: + + .. sourcecode:: jinja + + {{ "%s - %s"|format("Hello?", "Foo!") }} + -> Hello? - Foo! + """ + if args and kwargs: + raise FilterArgumentError('can\'t handle positional and keyword ' + 'arguments at the same time') + return soft_unicode(value) % (kwargs or args) + + +def do_trim(value): + """Strip leading and trailing whitespace.""" + return soft_unicode(value).strip() + + +def do_striptags(value): + """Strip SGML/XML tags and replace adjacent whitespace by one space. + """ + if hasattr(value, '__html__'): + value = value.__html__() + return Markup(text_type(value)).striptags() + + +def do_slice(value, slices, fill_with=None): + """Slice an iterator and return a list of lists containing + those items. Useful if you want to create a div containing + three ul tags that represent columns: + + .. sourcecode:: html+jinja + + <div class="columwrapper"> + {%- for column in items|slice(3) %} + <ul class="column-{{ loop.index }}"> + {%- for item in column %} + <li>{{ item }}</li> + {%- endfor %} + </ul> + {%- endfor %} + </div> + + If you pass it a second argument it's used to fill missing + values on the last iteration. + """ + seq = list(value) + length = len(seq) + items_per_slice = length // slices + slices_with_extra = length % slices + offset = 0 + for slice_number in range(slices): + start = offset + slice_number * items_per_slice + if slice_number < slices_with_extra: + offset += 1 + end = offset + (slice_number + 1) * items_per_slice + tmp = seq[start:end] + if fill_with is not None and slice_number >= slices_with_extra: + tmp.append(fill_with) + yield tmp + + +def do_batch(value, linecount, fill_with=None): + """ + A filter that batches items. It works pretty much like `slice` + just the other way round. It returns a list of lists with the + given number of items. If you provide a second parameter this + is used to fill up missing items. See this example: + + .. sourcecode:: html+jinja + + <table> + {%- for row in items|batch(3, ' ') %} + <tr> + {%- for column in row %} + <td>{{ column }}</td> + {%- endfor %} + </tr> + {%- endfor %} + </table> + """ + result = [] + tmp = [] + for item in value: + if len(tmp) == linecount: + yield tmp + tmp = [] + tmp.append(item) + if tmp: + if fill_with is not None and len(tmp) < linecount: + tmp += [fill_with] * (linecount - len(tmp)) + yield tmp + + +def do_round(value, precision=0, method='common'): + """Round the number to a given precision. The first + parameter specifies the precision (default is ``0``), the + second the rounding method: + + - ``'common'`` rounds either up or down + - ``'ceil'`` always rounds up + - ``'floor'`` always rounds down + + If you don't specify a method ``'common'`` is used. + + .. sourcecode:: jinja + + {{ 42.55|round }} + -> 43.0 + {{ 42.55|round(1, 'floor') }} + -> 42.5 + + Note that even if rounded to 0 precision, a float is returned. If + you need a real integer, pipe it through `int`: + + .. sourcecode:: jinja + + {{ 42.55|round|int }} + -> 43 + """ + if not method in ('common', 'ceil', 'floor'): + raise FilterArgumentError('method must be common, ceil or floor') + if method == 'common': + return round(value, precision) + func = getattr(math, method) + return func(value * (10 ** precision)) / (10 ** precision) + + +@environmentfilter +def do_groupby(environment, value, attribute): + """Group a sequence of objects by a common attribute. + + If you for example have a list of dicts or objects that represent persons + with `gender`, `first_name` and `last_name` attributes and you want to + group all users by genders you can do something like the following + snippet: + + .. sourcecode:: html+jinja + + <ul> + {% for group in persons|groupby('gender') %} + <li>{{ group.grouper }}<ul> + {% for person in group.list %} + <li>{{ person.first_name }} {{ person.last_name }}</li> + {% endfor %}</ul></li> + {% endfor %} + </ul> + + Additionally it's possible to use tuple unpacking for the grouper and + list: + + .. sourcecode:: html+jinja + + <ul> + {% for grouper, list in persons|groupby('gender') %} + ... + {% endfor %} + </ul> + + As you can see the item we're grouping by is stored in the `grouper` + attribute and the `list` contains all the objects that have this grouper + in common. + + .. versionchanged:: 2.6 + It's now possible to use dotted notation to group by the child + attribute of another attribute. + """ + expr = make_attrgetter(environment, attribute) + return sorted(map(_GroupTuple, groupby(sorted(value, key=expr), expr))) + + +class _GroupTuple(tuple): + __slots__ = () + grouper = property(itemgetter(0)) + list = property(itemgetter(1)) + + def __new__(cls, xxx_todo_changeme): + (key, value) = xxx_todo_changeme + return tuple.__new__(cls, (key, list(value))) + + +@environmentfilter +def do_sum(environment, iterable, attribute=None, start=0): + """Returns the sum of a sequence of numbers plus the value of parameter + 'start' (which defaults to 0). When the sequence is empty it returns + start. + + It is also possible to sum up only certain attributes: + + .. sourcecode:: jinja + + Total: {{ items|sum(attribute='price') }} + + .. versionchanged:: 2.6 + The `attribute` parameter was added to allow suming up over + attributes. Also the `start` parameter was moved on to the right. + """ + if attribute is not None: + iterable = imap(make_attrgetter(environment, attribute), iterable) + return sum(iterable, start) + + +def do_list(value): + """Convert the value into a list. If it was a string the returned list + will be a list of characters. + """ + return list(value) + + +def do_mark_safe(value): + """Mark the value as safe which means that in an environment with automatic + escaping enabled this variable will not be escaped. + """ + return Markup(value) + + +def do_mark_unsafe(value): + """Mark a value as unsafe. This is the reverse operation for :func:`safe`.""" + return text_type(value) + + +def do_reverse(value): + """Reverse the object or return an iterator the iterates over it the other + way round. + """ + if isinstance(value, string_types): + return value[::-1] + try: + return reversed(value) + except TypeError: + try: + rv = list(value) + rv.reverse() + return rv + except TypeError: + raise FilterArgumentError('argument must be iterable') + + +@environmentfilter +def do_attr(environment, obj, name): + """Get an attribute of an object. ``foo|attr("bar")`` works like + ``foo["bar"]`` just that always an attribute is returned and items are not + looked up. + + See :ref:`Notes on subscriptions <notes-on-subscriptions>` for more details. + """ + try: + name = str(name) + except UnicodeError: + pass + else: + try: + value = getattr(obj, name) + except AttributeError: + pass + else: + if environment.sandboxed and not \ + environment.is_safe_attribute(obj, name, value): + return environment.unsafe_undefined(obj, name) + return value + return environment.undefined(obj=obj, name=name) + + +@contextfilter +def do_map(*args, **kwargs): + """Applies a filter on a sequence of objects or looks up an attribute. + This is useful when dealing with lists of objects but you are really + only interested in a certain value of it. + + The basic usage is mapping on an attribute. Imagine you have a list + of users but you are only interested in a list of usernames: + + .. sourcecode:: jinja + + Users on this page: {{ users|map(attribute='username')|join(', ') }} + + Alternatively you can let it invoke a filter by passing the name of the + filter and the arguments afterwards. A good example would be applying a + text conversion filter on a sequence: + + .. sourcecode:: jinja + + Users on this page: {{ titles|map('lower')|join(', ') }} + + .. versionadded:: 2.7 + """ + context = args[0] + seq = args[1] + + if len(args) == 2 and 'attribute' in kwargs: + attribute = kwargs.pop('attribute') + if kwargs: + raise FilterArgumentError('Unexpected keyword argument %r' % + next(iter(kwargs))) + func = make_attrgetter(context.environment, attribute) + else: + try: + name = args[2] + args = args[3:] + except LookupError: + raise FilterArgumentError('map requires a filter argument') + func = lambda item: context.environment.call_filter( + name, item, args, kwargs, context=context) + + if seq: + for item in seq: + yield func(item) + + +@contextfilter +def do_select(*args, **kwargs): + """Filters a sequence of objects by appying a test to either the object + or the attribute and only selecting the ones with the test succeeding. + + Example usage: + + .. sourcecode:: jinja + + {{ numbers|select("odd") }} + + .. versionadded:: 2.7 + """ + return _select_or_reject(args, kwargs, lambda x: x, False) + + +@contextfilter +def do_reject(*args, **kwargs): + """Filters a sequence of objects by appying a test to either the object + or the attribute and rejecting the ones with the test succeeding. + + Example usage: + + .. sourcecode:: jinja + + {{ numbers|reject("odd") }} + + .. versionadded:: 2.7 + """ + return _select_or_reject(args, kwargs, lambda x: not x, False) + + +@contextfilter +def do_selectattr(*args, **kwargs): + """Filters a sequence of objects by appying a test to either the object + or the attribute and only selecting the ones with the test succeeding. + + Example usage: + + .. sourcecode:: jinja + + {{ users|selectattr("is_active") }} + {{ users|selectattr("email", "none") }} + + .. versionadded:: 2.7 + """ + return _select_or_reject(args, kwargs, lambda x: x, True) + + +@contextfilter +def do_rejectattr(*args, **kwargs): + """Filters a sequence of objects by appying a test to either the object + or the attribute and rejecting the ones with the test succeeding. + + .. sourcecode:: jinja + + {{ users|rejectattr("is_active") }} + {{ users|rejectattr("email", "none") }} + + .. versionadded:: 2.7 + """ + return _select_or_reject(args, kwargs, lambda x: not x, True) + + +def _select_or_reject(args, kwargs, modfunc, lookup_attr): + context = args[0] + seq = args[1] + if lookup_attr: + try: + attr = args[2] + except LookupError: + raise FilterArgumentError('Missing parameter for attribute name') + transfunc = make_attrgetter(context.environment, attr) + off = 1 + else: + off = 0 + transfunc = lambda x: x + + try: + name = args[2 + off] + args = args[3 + off:] + func = lambda item: context.environment.call_test( + name, item, args, kwargs) + except LookupError: + func = bool + + if seq: + for item in seq: + if modfunc(func(transfunc(item))): + yield item + + +FILTERS = { + 'attr': do_attr, + 'replace': do_replace, + 'upper': do_upper, + 'lower': do_lower, + 'escape': escape, + 'e': escape, + 'forceescape': do_forceescape, + 'capitalize': do_capitalize, + 'title': do_title, + 'default': do_default, + 'd': do_default, + 'join': do_join, + 'count': len, + 'dictsort': do_dictsort, + 'sort': do_sort, + 'length': len, + 'reverse': do_reverse, + 'center': do_center, + 'indent': do_indent, + 'title': do_title, + 'capitalize': do_capitalize, + 'first': do_first, + 'last': do_last, + 'map': do_map, + 'random': do_random, + 'reject': do_reject, + 'rejectattr': do_rejectattr, + 'filesizeformat': do_filesizeformat, + 'pprint': do_pprint, + 'truncate': do_truncate, + 'wordwrap': do_wordwrap, + 'wordcount': do_wordcount, + 'int': do_int, + 'float': do_float, + 'string': soft_unicode, + 'list': do_list, + 'urlize': do_urlize, + 'format': do_format, + 'trim': do_trim, + 'striptags': do_striptags, + 'select': do_select, + 'selectattr': do_selectattr, + 'slice': do_slice, + 'batch': do_batch, + 'sum': do_sum, + 'abs': abs, + 'round': do_round, + 'groupby': do_groupby, + 'safe': do_mark_safe, + 'xmlattr': do_xmlattr, + 'urlencode': do_urlencode +} diff --git a/mojo/public/third_party/jinja2/get_jinja2.sh b/mojo/public/third_party/jinja2/get_jinja2.sh new file mode 100755 index 0000000..9502146 --- /dev/null +++ b/mojo/public/third_party/jinja2/get_jinja2.sh @@ -0,0 +1,123 @@ +#!/bin/bash +# Download and extract Jinja2 +# Homepage: +# http://jinja.pocoo.org/ +# Installation instructions: +# http://jinja.pocoo.org/docs/intro/#from-the-tarball-release +# Download page: +# https://pypi.python.org/pypi/Jinja2 +PACKAGE='Jinja2' +VERSION='2.7.1' +PACKAGE_DIR='jinja2' + +CHROMIUM_FILES="README.chromium OWNERS get_jinja2.sh" +EXTRA_FILES='LICENSE AUTHORS' +REMOVE_FILES='testsuite' + +SRC_URL='https://pypi.python.org/packages/source/' +SRC_URL+="${PACKAGE:0:1}/$PACKAGE/$PACKAGE-$VERSION.tar.gz" +FILENAME="$(basename $SRC_URL)" +MD5_FILENAME="$FILENAME.md5" +SHA512_FILENAME="$FILENAME.sha512" +CHROMIUM_FILES+=" $MD5_FILENAME $SHA512_FILENAME" + +BUILD_DIR="$PACKAGE-$VERSION" +THIRD_PARTY="$(dirname $(realpath $(dirname "${BASH_SOURCE[0]}")))" +INSTALL_DIR="$THIRD_PARTY/$PACKAGE_DIR" +OUT_DIR="$INSTALL_DIR/$BUILD_DIR/$PACKAGE_DIR" +OLD_DIR="$THIRD_PARTY/$PACKAGE_DIR.old" + +function check_hashes { + # Hashes generated via: + # FILENAME=Jinja2-2.7.1.tar.gz + # md5sum "$FILENAME" > "$FILENAME.md5" + # sha512sum "$FILENAME" > "$FILENAME.sha512" + # unset FILENAME + + # MD5 + if ! [ -f "$MD5_FILENAME" ] + then + echo "MD5 hash file $MD5_FILENAME not found, could not verify archive" + exit 1 + fi + + # 32-digit hash, followed by filename + MD5_HASHFILE_REGEX="^[0-9a-f]{32} $FILENAME" + if ! grep --extended-regex --line-regex --silent \ + "$MD5_HASHFILE_REGEX" "$MD5_FILENAME" + then + echo "MD5 hash file $MD5_FILENAME does not contain hash for $FILENAME," \ + 'could not verify archive' + echo 'Hash file contents are:' + cat "$MD5_FILENAME" + exit 1 + fi + + if ! md5sum --check "$MD5_FILENAME" + then + echo 'MD5 hash does not match,' \ + "archive file $FILENAME corrupt or compromised!" + exit 1 + fi + + # SHA-512 + if ! [ -f "$SHA512_FILENAME" ] + then + echo "SHA-512 hash file $SHA512_FILENAME not found," \ + 'could not verify archive' + exit 1 + fi + + # 128-digit hash, followed by filename + SHA512_HASHFILE_REGEX="^[0-9a-f]{128} $FILENAME" + if ! grep --extended-regex --line-regex --silent \ + "$SHA512_HASHFILE_REGEX" "$SHA512_FILENAME" + then + echo "SHA-512 hash file $SHA512_FILENAME does not contain hash for" \ + "$FILENAME, could not verify archive" + echo 'Hash file contents are:' + cat "$SHA512_FILENAME" + exit 1 + fi + + if ! sha512sum --check "$SHA512_FILENAME" + then + echo 'SHA-512 hash does not match,' \ + "archive file $FILENAME corrupt or compromised!" + exit 1 + fi +} + + +################################################################################ +# Body + +cd "$INSTALL_DIR" +echo "Downloading $SRC_URL" +curl --remote-name "$SRC_URL" +check_hashes +tar xvzf "$FILENAME" +# Copy extra files over +for FILE in $CHROMIUM_FILES +do + cp "$FILE" "$OUT_DIR" +done + +cd "$BUILD_DIR" +for FILE in $EXTRA_FILES +do + cp "$FILE" "$OUT_DIR" +done + +cd "$OUT_DIR" +for FILE in $REMOVE_FILES +do + rm -fr "$FILE" +done + +# Replace with new directory +cd .. +mv "$INSTALL_DIR" "$OLD_DIR" +mv "$PACKAGE_DIR" "$INSTALL_DIR" +cd "$INSTALL_DIR" +rm -fr "$OLD_DIR" diff --git a/mojo/public/third_party/jinja2/lexer.py b/mojo/public/third_party/jinja2/lexer.py new file mode 100644 index 0000000..a501285 --- /dev/null +++ b/mojo/public/third_party/jinja2/lexer.py @@ -0,0 +1,733 @@ +# -*- coding: utf-8 -*- +""" + jinja2.lexer + ~~~~~~~~~~~~ + + This module implements a Jinja / Python combination lexer. The + `Lexer` class provided by this module is used to do some preprocessing + for Jinja. + + On the one hand it filters out invalid operators like the bitshift + operators we don't allow in templates. On the other hand it separates + template code and python code in expressions. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import re + +from operator import itemgetter +from collections import deque +from jinja2.exceptions import TemplateSyntaxError +from jinja2.utils import LRUCache +from jinja2._compat import next, iteritems, implements_iterator, text_type, \ + intern + + +# cache for the lexers. Exists in order to be able to have multiple +# environments with the same lexer +_lexer_cache = LRUCache(50) + +# static regular expressions +whitespace_re = re.compile(r'\s+', re.U) +string_re = re.compile(r"('([^'\\]*(?:\\.[^'\\]*)*)'" + r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S) +integer_re = re.compile(r'\d+') + +# we use the unicode identifier rule if this python version is able +# to handle unicode identifiers, otherwise the standard ASCII one. +try: + compile('föö', '<unknown>', 'eval') +except SyntaxError: + name_re = re.compile(r'\b[a-zA-Z_][a-zA-Z0-9_]*\b') +else: + from jinja2 import _stringdefs + name_re = re.compile(r'[%s][%s]*' % (_stringdefs.xid_start, + _stringdefs.xid_continue)) + +float_re = re.compile(r'(?<!\.)\d+\.\d+') +newline_re = re.compile(r'(\r\n|\r|\n)') + +# internal the tokens and keep references to them +TOKEN_ADD = intern('add') +TOKEN_ASSIGN = intern('assign') +TOKEN_COLON = intern('colon') +TOKEN_COMMA = intern('comma') +TOKEN_DIV = intern('div') +TOKEN_DOT = intern('dot') +TOKEN_EQ = intern('eq') +TOKEN_FLOORDIV = intern('floordiv') +TOKEN_GT = intern('gt') +TOKEN_GTEQ = intern('gteq') +TOKEN_LBRACE = intern('lbrace') +TOKEN_LBRACKET = intern('lbracket') +TOKEN_LPAREN = intern('lparen') +TOKEN_LT = intern('lt') +TOKEN_LTEQ = intern('lteq') +TOKEN_MOD = intern('mod') +TOKEN_MUL = intern('mul') +TOKEN_NE = intern('ne') +TOKEN_PIPE = intern('pipe') +TOKEN_POW = intern('pow') +TOKEN_RBRACE = intern('rbrace') +TOKEN_RBRACKET = intern('rbracket') +TOKEN_RPAREN = intern('rparen') +TOKEN_SEMICOLON = intern('semicolon') +TOKEN_SUB = intern('sub') +TOKEN_TILDE = intern('tilde') +TOKEN_WHITESPACE = intern('whitespace') +TOKEN_FLOAT = intern('float') +TOKEN_INTEGER = intern('integer') +TOKEN_NAME = intern('name') +TOKEN_STRING = intern('string') +TOKEN_OPERATOR = intern('operator') +TOKEN_BLOCK_BEGIN = intern('block_begin') +TOKEN_BLOCK_END = intern('block_end') +TOKEN_VARIABLE_BEGIN = intern('variable_begin') +TOKEN_VARIABLE_END = intern('variable_end') +TOKEN_RAW_BEGIN = intern('raw_begin') +TOKEN_RAW_END = intern('raw_end') +TOKEN_COMMENT_BEGIN = intern('comment_begin') +TOKEN_COMMENT_END = intern('comment_end') +TOKEN_COMMENT = intern('comment') +TOKEN_LINESTATEMENT_BEGIN = intern('linestatement_begin') +TOKEN_LINESTATEMENT_END = intern('linestatement_end') +TOKEN_LINECOMMENT_BEGIN = intern('linecomment_begin') +TOKEN_LINECOMMENT_END = intern('linecomment_end') +TOKEN_LINECOMMENT = intern('linecomment') +TOKEN_DATA = intern('data') +TOKEN_INITIAL = intern('initial') +TOKEN_EOF = intern('eof') + +# bind operators to token types +operators = { + '+': TOKEN_ADD, + '-': TOKEN_SUB, + '/': TOKEN_DIV, + '//': TOKEN_FLOORDIV, + '*': TOKEN_MUL, + '%': TOKEN_MOD, + '**': TOKEN_POW, + '~': TOKEN_TILDE, + '[': TOKEN_LBRACKET, + ']': TOKEN_RBRACKET, + '(': TOKEN_LPAREN, + ')': TOKEN_RPAREN, + '{': TOKEN_LBRACE, + '}': TOKEN_RBRACE, + '==': TOKEN_EQ, + '!=': TOKEN_NE, + '>': TOKEN_GT, + '>=': TOKEN_GTEQ, + '<': TOKEN_LT, + '<=': TOKEN_LTEQ, + '=': TOKEN_ASSIGN, + '.': TOKEN_DOT, + ':': TOKEN_COLON, + '|': TOKEN_PIPE, + ',': TOKEN_COMMA, + ';': TOKEN_SEMICOLON +} + +reverse_operators = dict([(v, k) for k, v in iteritems(operators)]) +assert len(operators) == len(reverse_operators), 'operators dropped' +operator_re = re.compile('(%s)' % '|'.join(re.escape(x) for x in + sorted(operators, key=lambda x: -len(x)))) + +ignored_tokens = frozenset([TOKEN_COMMENT_BEGIN, TOKEN_COMMENT, + TOKEN_COMMENT_END, TOKEN_WHITESPACE, + TOKEN_WHITESPACE, TOKEN_LINECOMMENT_BEGIN, + TOKEN_LINECOMMENT_END, TOKEN_LINECOMMENT]) +ignore_if_empty = frozenset([TOKEN_WHITESPACE, TOKEN_DATA, + TOKEN_COMMENT, TOKEN_LINECOMMENT]) + + +def _describe_token_type(token_type): + if token_type in reverse_operators: + return reverse_operators[token_type] + return { + TOKEN_COMMENT_BEGIN: 'begin of comment', + TOKEN_COMMENT_END: 'end of comment', + TOKEN_COMMENT: 'comment', + TOKEN_LINECOMMENT: 'comment', + TOKEN_BLOCK_BEGIN: 'begin of statement block', + TOKEN_BLOCK_END: 'end of statement block', + TOKEN_VARIABLE_BEGIN: 'begin of print statement', + TOKEN_VARIABLE_END: 'end of print statement', + TOKEN_LINESTATEMENT_BEGIN: 'begin of line statement', + TOKEN_LINESTATEMENT_END: 'end of line statement', + TOKEN_DATA: 'template data / text', + TOKEN_EOF: 'end of template' + }.get(token_type, token_type) + + +def describe_token(token): + """Returns a description of the token.""" + if token.type == 'name': + return token.value + return _describe_token_type(token.type) + + +def describe_token_expr(expr): + """Like `describe_token` but for token expressions.""" + if ':' in expr: + type, value = expr.split(':', 1) + if type == 'name': + return value + else: + type = expr + return _describe_token_type(type) + + +def count_newlines(value): + """Count the number of newline characters in the string. This is + useful for extensions that filter a stream. + """ + return len(newline_re.findall(value)) + + +def compile_rules(environment): + """Compiles all the rules from the environment into a list of rules.""" + e = re.escape + rules = [ + (len(environment.comment_start_string), 'comment', + e(environment.comment_start_string)), + (len(environment.block_start_string), 'block', + e(environment.block_start_string)), + (len(environment.variable_start_string), 'variable', + e(environment.variable_start_string)) + ] + + if environment.line_statement_prefix is not None: + rules.append((len(environment.line_statement_prefix), 'linestatement', + r'^[ \t\v]*' + e(environment.line_statement_prefix))) + if environment.line_comment_prefix is not None: + rules.append((len(environment.line_comment_prefix), 'linecomment', + r'(?:^|(?<=\S))[^\S\r\n]*' + + e(environment.line_comment_prefix))) + + return [x[1:] for x in sorted(rules, reverse=True)] + + +class Failure(object): + """Class that raises a `TemplateSyntaxError` if called. + Used by the `Lexer` to specify known errors. + """ + + def __init__(self, message, cls=TemplateSyntaxError): + self.message = message + self.error_class = cls + + def __call__(self, lineno, filename): + raise self.error_class(self.message, lineno, filename) + + +class Token(tuple): + """Token class.""" + __slots__ = () + lineno, type, value = (property(itemgetter(x)) for x in range(3)) + + def __new__(cls, lineno, type, value): + return tuple.__new__(cls, (lineno, intern(str(type)), value)) + + def __str__(self): + if self.type in reverse_operators: + return reverse_operators[self.type] + elif self.type == 'name': + return self.value + return self.type + + def test(self, expr): + """Test a token against a token expression. This can either be a + token type or ``'token_type:token_value'``. This can only test + against string values and types. + """ + # here we do a regular string equality check as test_any is usually + # passed an iterable of not interned strings. + if self.type == expr: + return True + elif ':' in expr: + return expr.split(':', 1) == [self.type, self.value] + return False + + def test_any(self, *iterable): + """Test against multiple token expressions.""" + for expr in iterable: + if self.test(expr): + return True + return False + + def __repr__(self): + return 'Token(%r, %r, %r)' % ( + self.lineno, + self.type, + self.value + ) + + +@implements_iterator +class TokenStreamIterator(object): + """The iterator for tokenstreams. Iterate over the stream + until the eof token is reached. + """ + + def __init__(self, stream): + self.stream = stream + + def __iter__(self): + return self + + def __next__(self): + token = self.stream.current + if token.type is TOKEN_EOF: + self.stream.close() + raise StopIteration() + next(self.stream) + return token + + +@implements_iterator +class TokenStream(object): + """A token stream is an iterable that yields :class:`Token`\s. The + parser however does not iterate over it but calls :meth:`next` to go + one token ahead. The current active token is stored as :attr:`current`. + """ + + def __init__(self, generator, name, filename): + self._iter = iter(generator) + self._pushed = deque() + self.name = name + self.filename = filename + self.closed = False + self.current = Token(1, TOKEN_INITIAL, '') + next(self) + + def __iter__(self): + return TokenStreamIterator(self) + + def __bool__(self): + return bool(self._pushed) or self.current.type is not TOKEN_EOF + __nonzero__ = __bool__ # py2 + + eos = property(lambda x: not x, doc="Are we at the end of the stream?") + + def push(self, token): + """Push a token back to the stream.""" + self._pushed.append(token) + + def look(self): + """Look at the next token.""" + old_token = next(self) + result = self.current + self.push(result) + self.current = old_token + return result + + def skip(self, n=1): + """Got n tokens ahead.""" + for x in range(n): + next(self) + + def next_if(self, expr): + """Perform the token test and return the token if it matched. + Otherwise the return value is `None`. + """ + if self.current.test(expr): + return next(self) + + def skip_if(self, expr): + """Like :meth:`next_if` but only returns `True` or `False`.""" + return self.next_if(expr) is not None + + def __next__(self): + """Go one token ahead and return the old one""" + rv = self.current + if self._pushed: + self.current = self._pushed.popleft() + elif self.current.type is not TOKEN_EOF: + try: + self.current = next(self._iter) + except StopIteration: + self.close() + return rv + + def close(self): + """Close the stream.""" + self.current = Token(self.current.lineno, TOKEN_EOF, '') + self._iter = None + self.closed = True + + def expect(self, expr): + """Expect a given token type and return it. This accepts the same + argument as :meth:`jinja2.lexer.Token.test`. + """ + if not self.current.test(expr): + expr = describe_token_expr(expr) + if self.current.type is TOKEN_EOF: + raise TemplateSyntaxError('unexpected end of template, ' + 'expected %r.' % expr, + self.current.lineno, + self.name, self.filename) + raise TemplateSyntaxError("expected token %r, got %r" % + (expr, describe_token(self.current)), + self.current.lineno, + self.name, self.filename) + try: + return self.current + finally: + next(self) + + +def get_lexer(environment): + """Return a lexer which is probably cached.""" + key = (environment.block_start_string, + environment.block_end_string, + environment.variable_start_string, + environment.variable_end_string, + environment.comment_start_string, + environment.comment_end_string, + environment.line_statement_prefix, + environment.line_comment_prefix, + environment.trim_blocks, + environment.lstrip_blocks, + environment.newline_sequence, + environment.keep_trailing_newline) + lexer = _lexer_cache.get(key) + if lexer is None: + lexer = Lexer(environment) + _lexer_cache[key] = lexer + return lexer + + +class Lexer(object): + """Class that implements a lexer for a given environment. Automatically + created by the environment class, usually you don't have to do that. + + Note that the lexer is not automatically bound to an environment. + Multiple environments can share the same lexer. + """ + + def __init__(self, environment): + # shortcuts + c = lambda x: re.compile(x, re.M | re.S) + e = re.escape + + # lexing rules for tags + tag_rules = [ + (whitespace_re, TOKEN_WHITESPACE, None), + (float_re, TOKEN_FLOAT, None), + (integer_re, TOKEN_INTEGER, None), + (name_re, TOKEN_NAME, None), + (string_re, TOKEN_STRING, None), + (operator_re, TOKEN_OPERATOR, None) + ] + + # assemble the root lexing rule. because "|" is ungreedy + # we have to sort by length so that the lexer continues working + # as expected when we have parsing rules like <% for block and + # <%= for variables. (if someone wants asp like syntax) + # variables are just part of the rules if variable processing + # is required. + root_tag_rules = compile_rules(environment) + + # block suffix if trimming is enabled + block_suffix_re = environment.trim_blocks and '\\n?' or '' + + # strip leading spaces if lstrip_blocks is enabled + prefix_re = {} + if environment.lstrip_blocks: + # use '{%+' to manually disable lstrip_blocks behavior + no_lstrip_re = e('+') + # detect overlap between block and variable or comment strings + block_diff = c(r'^%s(.*)' % e(environment.block_start_string)) + # make sure we don't mistake a block for a variable or a comment + m = block_diff.match(environment.comment_start_string) + no_lstrip_re += m and r'|%s' % e(m.group(1)) or '' + m = block_diff.match(environment.variable_start_string) + no_lstrip_re += m and r'|%s' % e(m.group(1)) or '' + + # detect overlap between comment and variable strings + comment_diff = c(r'^%s(.*)' % e(environment.comment_start_string)) + m = comment_diff.match(environment.variable_start_string) + no_variable_re = m and r'(?!%s)' % e(m.group(1)) or '' + + lstrip_re = r'^[ \t]*' + block_prefix_re = r'%s%s(?!%s)|%s\+?' % ( + lstrip_re, + e(environment.block_start_string), + no_lstrip_re, + e(environment.block_start_string), + ) + comment_prefix_re = r'%s%s%s|%s\+?' % ( + lstrip_re, + e(environment.comment_start_string), + no_variable_re, + e(environment.comment_start_string), + ) + prefix_re['block'] = block_prefix_re + prefix_re['comment'] = comment_prefix_re + else: + block_prefix_re = '%s' % e(environment.block_start_string) + + self.newline_sequence = environment.newline_sequence + self.keep_trailing_newline = environment.keep_trailing_newline + + # global lexing rules + self.rules = { + 'root': [ + # directives + (c('(.*?)(?:%s)' % '|'.join( + [r'(?P<raw_begin>(?:\s*%s\-|%s)\s*raw\s*(?:\-%s\s*|%s))' % ( + e(environment.block_start_string), + block_prefix_re, + e(environment.block_end_string), + e(environment.block_end_string) + )] + [ + r'(?P<%s_begin>\s*%s\-|%s)' % (n, r, prefix_re.get(n,r)) + for n, r in root_tag_rules + ])), (TOKEN_DATA, '#bygroup'), '#bygroup'), + # data + (c('.+'), TOKEN_DATA, None) + ], + # comments + TOKEN_COMMENT_BEGIN: [ + (c(r'(.*?)((?:\-%s\s*|%s)%s)' % ( + e(environment.comment_end_string), + e(environment.comment_end_string), + block_suffix_re + )), (TOKEN_COMMENT, TOKEN_COMMENT_END), '#pop'), + (c('(.)'), (Failure('Missing end of comment tag'),), None) + ], + # blocks + TOKEN_BLOCK_BEGIN: [ + (c('(?:\-%s\s*|%s)%s' % ( + e(environment.block_end_string), + e(environment.block_end_string), + block_suffix_re + )), TOKEN_BLOCK_END, '#pop'), + ] + tag_rules, + # variables + TOKEN_VARIABLE_BEGIN: [ + (c('\-%s\s*|%s' % ( + e(environment.variable_end_string), + e(environment.variable_end_string) + )), TOKEN_VARIABLE_END, '#pop') + ] + tag_rules, + # raw block + TOKEN_RAW_BEGIN: [ + (c('(.*?)((?:\s*%s\-|%s)\s*endraw\s*(?:\-%s\s*|%s%s))' % ( + e(environment.block_start_string), + block_prefix_re, + e(environment.block_end_string), + e(environment.block_end_string), + block_suffix_re + )), (TOKEN_DATA, TOKEN_RAW_END), '#pop'), + (c('(.)'), (Failure('Missing end of raw directive'),), None) + ], + # line statements + TOKEN_LINESTATEMENT_BEGIN: [ + (c(r'\s*(\n|$)'), TOKEN_LINESTATEMENT_END, '#pop') + ] + tag_rules, + # line comments + TOKEN_LINECOMMENT_BEGIN: [ + (c(r'(.*?)()(?=\n|$)'), (TOKEN_LINECOMMENT, + TOKEN_LINECOMMENT_END), '#pop') + ] + } + + def _normalize_newlines(self, value): + """Called for strings and template data to normalize it to unicode.""" + return newline_re.sub(self.newline_sequence, value) + + def tokenize(self, source, name=None, filename=None, state=None): + """Calls tokeniter + tokenize and wraps it in a token stream. + """ + stream = self.tokeniter(source, name, filename, state) + return TokenStream(self.wrap(stream, name, filename), name, filename) + + def wrap(self, stream, name=None, filename=None): + """This is called with the stream as returned by `tokenize` and wraps + every token in a :class:`Token` and converts the value. + """ + for lineno, token, value in stream: + if token in ignored_tokens: + continue + elif token == 'linestatement_begin': + token = 'block_begin' + elif token == 'linestatement_end': + token = 'block_end' + # we are not interested in those tokens in the parser + elif token in ('raw_begin', 'raw_end'): + continue + elif token == 'data': + value = self._normalize_newlines(value) + elif token == 'keyword': + token = value + elif token == 'name': + value = str(value) + elif token == 'string': + # try to unescape string + try: + value = self._normalize_newlines(value[1:-1]) \ + .encode('ascii', 'backslashreplace') \ + .decode('unicode-escape') + except Exception as e: + msg = str(e).split(':')[-1].strip() + raise TemplateSyntaxError(msg, lineno, name, filename) + # if we can express it as bytestring (ascii only) + # we do that for support of semi broken APIs + # as datetime.datetime.strftime. On python 3 this + # call becomes a noop thanks to 2to3 + try: + value = str(value) + except UnicodeError: + pass + elif token == 'integer': + value = int(value) + elif token == 'float': + value = float(value) + elif token == 'operator': + token = operators[value] + yield Token(lineno, token, value) + + def tokeniter(self, source, name, filename=None, state=None): + """This method tokenizes the text and returns the tokens in a + generator. Use this method if you just want to tokenize a template. + """ + source = text_type(source) + lines = source.splitlines() + if self.keep_trailing_newline and source: + for newline in ('\r\n', '\r', '\n'): + if source.endswith(newline): + lines.append('') + break + source = '\n'.join(lines) + pos = 0 + lineno = 1 + stack = ['root'] + if state is not None and state != 'root': + assert state in ('variable', 'block'), 'invalid state' + stack.append(state + '_begin') + else: + state = 'root' + statetokens = self.rules[stack[-1]] + source_length = len(source) + + balancing_stack = [] + + while 1: + # tokenizer loop + for regex, tokens, new_state in statetokens: + m = regex.match(source, pos) + # if no match we try again with the next rule + if m is None: + continue + + # we only match blocks and variables if braces / parentheses + # are balanced. continue parsing with the lower rule which + # is the operator rule. do this only if the end tags look + # like operators + if balancing_stack and \ + tokens in ('variable_end', 'block_end', + 'linestatement_end'): + continue + + # tuples support more options + if isinstance(tokens, tuple): + for idx, token in enumerate(tokens): + # failure group + if token.__class__ is Failure: + raise token(lineno, filename) + # bygroup is a bit more complex, in that case we + # yield for the current token the first named + # group that matched + elif token == '#bygroup': + for key, value in iteritems(m.groupdict()): + if value is not None: + yield lineno, key, value + lineno += value.count('\n') + break + else: + raise RuntimeError('%r wanted to resolve ' + 'the token dynamically' + ' but no group matched' + % regex) + # normal group + else: + data = m.group(idx + 1) + if data or token not in ignore_if_empty: + yield lineno, token, data + lineno += data.count('\n') + + # strings as token just are yielded as it. + else: + data = m.group() + # update brace/parentheses balance + if tokens == 'operator': + if data == '{': + balancing_stack.append('}') + elif data == '(': + balancing_stack.append(')') + elif data == '[': + balancing_stack.append(']') + elif data in ('}', ')', ']'): + if not balancing_stack: + raise TemplateSyntaxError('unexpected \'%s\'' % + data, lineno, name, + filename) + expected_op = balancing_stack.pop() + if expected_op != data: + raise TemplateSyntaxError('unexpected \'%s\', ' + 'expected \'%s\'' % + (data, expected_op), + lineno, name, + filename) + # yield items + if data or tokens not in ignore_if_empty: + yield lineno, tokens, data + lineno += data.count('\n') + + # fetch new position into new variable so that we can check + # if there is a internal parsing error which would result + # in an infinite loop + pos2 = m.end() + + # handle state changes + if new_state is not None: + # remove the uppermost state + if new_state == '#pop': + stack.pop() + # resolve the new state by group checking + elif new_state == '#bygroup': + for key, value in iteritems(m.groupdict()): + if value is not None: + stack.append(key) + break + else: + raise RuntimeError('%r wanted to resolve the ' + 'new state dynamically but' + ' no group matched' % + regex) + # direct state name given + else: + stack.append(new_state) + statetokens = self.rules[stack[-1]] + # we are still at the same position and no stack change. + # this means a loop without break condition, avoid that and + # raise error + elif pos2 == pos: + raise RuntimeError('%r yielded empty string without ' + 'stack change' % regex) + # publish new function and start again + pos = pos2 + break + # if loop terminated without break we haven't found a single match + # either we are at the end of the file or we have a problem + else: + # end of text + if pos >= source_length: + return + # something went wrong + raise TemplateSyntaxError('unexpected char %r at %d' % + (source[pos], pos), lineno, + name, filename) diff --git a/mojo/public/third_party/jinja2/loaders.py b/mojo/public/third_party/jinja2/loaders.py new file mode 100644 index 0000000..a9a2625 --- /dev/null +++ b/mojo/public/third_party/jinja2/loaders.py @@ -0,0 +1,471 @@ +# -*- coding: utf-8 -*- +""" + jinja2.loaders + ~~~~~~~~~~~~~~ + + Jinja loader classes. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import os +import sys +import weakref +from types import ModuleType +from os import path +from hashlib import sha1 +from jinja2.exceptions import TemplateNotFound +from jinja2.utils import open_if_exists, internalcode +from jinja2._compat import string_types, iteritems + + +def split_template_path(template): + """Split a path into segments and perform a sanity check. If it detects + '..' in the path it will raise a `TemplateNotFound` error. + """ + pieces = [] + for piece in template.split('/'): + if path.sep in piece \ + or (path.altsep and path.altsep in piece) or \ + piece == path.pardir: + raise TemplateNotFound(template) + elif piece and piece != '.': + pieces.append(piece) + return pieces + + +class BaseLoader(object): + """Baseclass for all loaders. Subclass this and override `get_source` to + implement a custom loading mechanism. The environment provides a + `get_template` method that calls the loader's `load` method to get the + :class:`Template` object. + + A very basic example for a loader that looks up templates on the file + system could look like this:: + + from jinja2 import BaseLoader, TemplateNotFound + from os.path import join, exists, getmtime + + class MyLoader(BaseLoader): + + def __init__(self, path): + self.path = path + + def get_source(self, environment, template): + path = join(self.path, template) + if not exists(path): + raise TemplateNotFound(template) + mtime = getmtime(path) + with file(path) as f: + source = f.read().decode('utf-8') + return source, path, lambda: mtime == getmtime(path) + """ + + #: if set to `False` it indicates that the loader cannot provide access + #: to the source of templates. + #: + #: .. versionadded:: 2.4 + has_source_access = True + + def get_source(self, environment, template): + """Get the template source, filename and reload helper for a template. + It's passed the environment and template name and has to return a + tuple in the form ``(source, filename, uptodate)`` or raise a + `TemplateNotFound` error if it can't locate the template. + + The source part of the returned tuple must be the source of the + template as unicode string or a ASCII bytestring. The filename should + be the name of the file on the filesystem if it was loaded from there, + otherwise `None`. The filename is used by python for the tracebacks + if no loader extension is used. + + The last item in the tuple is the `uptodate` function. If auto + reloading is enabled it's always called to check if the template + changed. No arguments are passed so the function must store the + old state somewhere (for example in a closure). If it returns `False` + the template will be reloaded. + """ + if not self.has_source_access: + raise RuntimeError('%s cannot provide access to the source' % + self.__class__.__name__) + raise TemplateNotFound(template) + + def list_templates(self): + """Iterates over all templates. If the loader does not support that + it should raise a :exc:`TypeError` which is the default behavior. + """ + raise TypeError('this loader cannot iterate over all templates') + + @internalcode + def load(self, environment, name, globals=None): + """Loads a template. This method looks up the template in the cache + or loads one by calling :meth:`get_source`. Subclasses should not + override this method as loaders working on collections of other + loaders (such as :class:`PrefixLoader` or :class:`ChoiceLoader`) + will not call this method but `get_source` directly. + """ + code = None + if globals is None: + globals = {} + + # first we try to get the source for this template together + # with the filename and the uptodate function. + source, filename, uptodate = self.get_source(environment, name) + + # try to load the code from the bytecode cache if there is a + # bytecode cache configured. + bcc = environment.bytecode_cache + if bcc is not None: + bucket = bcc.get_bucket(environment, name, filename, source) + code = bucket.code + + # if we don't have code so far (not cached, no longer up to + # date) etc. we compile the template + if code is None: + code = environment.compile(source, name, filename) + + # if the bytecode cache is available and the bucket doesn't + # have a code so far, we give the bucket the new code and put + # it back to the bytecode cache. + if bcc is not None and bucket.code is None: + bucket.code = code + bcc.set_bucket(bucket) + + return environment.template_class.from_code(environment, code, + globals, uptodate) + + +class FileSystemLoader(BaseLoader): + """Loads templates from the file system. This loader can find templates + in folders on the file system and is the preferred way to load them. + + The loader takes the path to the templates as string, or if multiple + locations are wanted a list of them which is then looked up in the + given order: + + >>> loader = FileSystemLoader('/path/to/templates') + >>> loader = FileSystemLoader(['/path/to/templates', '/other/path']) + + Per default the template encoding is ``'utf-8'`` which can be changed + by setting the `encoding` parameter to something else. + """ + + def __init__(self, searchpath, encoding='utf-8'): + if isinstance(searchpath, string_types): + searchpath = [searchpath] + self.searchpath = list(searchpath) + self.encoding = encoding + + def get_source(self, environment, template): + pieces = split_template_path(template) + for searchpath in self.searchpath: + filename = path.join(searchpath, *pieces) + f = open_if_exists(filename) + if f is None: + continue + try: + contents = f.read().decode(self.encoding) + finally: + f.close() + + mtime = path.getmtime(filename) + def uptodate(): + try: + return path.getmtime(filename) == mtime + except OSError: + return False + return contents, filename, uptodate + raise TemplateNotFound(template) + + def list_templates(self): + found = set() + for searchpath in self.searchpath: + for dirpath, dirnames, filenames in os.walk(searchpath): + for filename in filenames: + template = os.path.join(dirpath, filename) \ + [len(searchpath):].strip(os.path.sep) \ + .replace(os.path.sep, '/') + if template[:2] == './': + template = template[2:] + if template not in found: + found.add(template) + return sorted(found) + + +class PackageLoader(BaseLoader): + """Load templates from python eggs or packages. It is constructed with + the name of the python package and the path to the templates in that + package:: + + loader = PackageLoader('mypackage', 'views') + + If the package path is not given, ``'templates'`` is assumed. + + Per default the template encoding is ``'utf-8'`` which can be changed + by setting the `encoding` parameter to something else. Due to the nature + of eggs it's only possible to reload templates if the package was loaded + from the file system and not a zip file. + """ + + def __init__(self, package_name, package_path='templates', + encoding='utf-8'): + from pkg_resources import DefaultProvider, ResourceManager, \ + get_provider + provider = get_provider(package_name) + self.encoding = encoding + self.manager = ResourceManager() + self.filesystem_bound = isinstance(provider, DefaultProvider) + self.provider = provider + self.package_path = package_path + + def get_source(self, environment, template): + pieces = split_template_path(template) + p = '/'.join((self.package_path,) + tuple(pieces)) + if not self.provider.has_resource(p): + raise TemplateNotFound(template) + + filename = uptodate = None + if self.filesystem_bound: + filename = self.provider.get_resource_filename(self.manager, p) + mtime = path.getmtime(filename) + def uptodate(): + try: + return path.getmtime(filename) == mtime + except OSError: + return False + + source = self.provider.get_resource_string(self.manager, p) + return source.decode(self.encoding), filename, uptodate + + def list_templates(self): + path = self.package_path + if path[:2] == './': + path = path[2:] + elif path == '.': + path = '' + offset = len(path) + results = [] + def _walk(path): + for filename in self.provider.resource_listdir(path): + fullname = path + '/' + filename + if self.provider.resource_isdir(fullname): + _walk(fullname) + else: + results.append(fullname[offset:].lstrip('/')) + _walk(path) + results.sort() + return results + + +class DictLoader(BaseLoader): + """Loads a template from a python dict. It's passed a dict of unicode + strings bound to template names. This loader is useful for unittesting: + + >>> loader = DictLoader({'index.html': 'source here'}) + + Because auto reloading is rarely useful this is disabled per default. + """ + + def __init__(self, mapping): + self.mapping = mapping + + def get_source(self, environment, template): + if template in self.mapping: + source = self.mapping[template] + return source, None, lambda: source == self.mapping.get(template) + raise TemplateNotFound(template) + + def list_templates(self): + return sorted(self.mapping) + + +class FunctionLoader(BaseLoader): + """A loader that is passed a function which does the loading. The + function becomes the name of the template passed and has to return either + an unicode string with the template source, a tuple in the form ``(source, + filename, uptodatefunc)`` or `None` if the template does not exist. + + >>> def load_template(name): + ... if name == 'index.html': + ... return '...' + ... + >>> loader = FunctionLoader(load_template) + + The `uptodatefunc` is a function that is called if autoreload is enabled + and has to return `True` if the template is still up to date. For more + details have a look at :meth:`BaseLoader.get_source` which has the same + return value. + """ + + def __init__(self, load_func): + self.load_func = load_func + + def get_source(self, environment, template): + rv = self.load_func(template) + if rv is None: + raise TemplateNotFound(template) + elif isinstance(rv, string_types): + return rv, None, None + return rv + + +class PrefixLoader(BaseLoader): + """A loader that is passed a dict of loaders where each loader is bound + to a prefix. The prefix is delimited from the template by a slash per + default, which can be changed by setting the `delimiter` argument to + something else:: + + loader = PrefixLoader({ + 'app1': PackageLoader('mypackage.app1'), + 'app2': PackageLoader('mypackage.app2') + }) + + By loading ``'app1/index.html'`` the file from the app1 package is loaded, + by loading ``'app2/index.html'`` the file from the second. + """ + + def __init__(self, mapping, delimiter='/'): + self.mapping = mapping + self.delimiter = delimiter + + def get_loader(self, template): + try: + prefix, name = template.split(self.delimiter, 1) + loader = self.mapping[prefix] + except (ValueError, KeyError): + raise TemplateNotFound(template) + return loader, name + + def get_source(self, environment, template): + loader, name = self.get_loader(template) + try: + return loader.get_source(environment, name) + except TemplateNotFound: + # re-raise the exception with the correct fileame here. + # (the one that includes the prefix) + raise TemplateNotFound(template) + + @internalcode + def load(self, environment, name, globals=None): + loader, local_name = self.get_loader(name) + try: + return loader.load(environment, local_name) + except TemplateNotFound: + # re-raise the exception with the correct fileame here. + # (the one that includes the prefix) + raise TemplateNotFound(name) + + def list_templates(self): + result = [] + for prefix, loader in iteritems(self.mapping): + for template in loader.list_templates(): + result.append(prefix + self.delimiter + template) + return result + + +class ChoiceLoader(BaseLoader): + """This loader works like the `PrefixLoader` just that no prefix is + specified. If a template could not be found by one loader the next one + is tried. + + >>> loader = ChoiceLoader([ + ... FileSystemLoader('/path/to/user/templates'), + ... FileSystemLoader('/path/to/system/templates') + ... ]) + + This is useful if you want to allow users to override builtin templates + from a different location. + """ + + def __init__(self, loaders): + self.loaders = loaders + + def get_source(self, environment, template): + for loader in self.loaders: + try: + return loader.get_source(environment, template) + except TemplateNotFound: + pass + raise TemplateNotFound(template) + + @internalcode + def load(self, environment, name, globals=None): + for loader in self.loaders: + try: + return loader.load(environment, name, globals) + except TemplateNotFound: + pass + raise TemplateNotFound(name) + + def list_templates(self): + found = set() + for loader in self.loaders: + found.update(loader.list_templates()) + return sorted(found) + + +class _TemplateModule(ModuleType): + """Like a normal module but with support for weak references""" + + +class ModuleLoader(BaseLoader): + """This loader loads templates from precompiled templates. + + Example usage: + + >>> loader = ChoiceLoader([ + ... ModuleLoader('/path/to/compiled/templates'), + ... FileSystemLoader('/path/to/templates') + ... ]) + + Templates can be precompiled with :meth:`Environment.compile_templates`. + """ + + has_source_access = False + + def __init__(self, path): + package_name = '_jinja2_module_templates_%x' % id(self) + + # create a fake module that looks for the templates in the + # path given. + mod = _TemplateModule(package_name) + if isinstance(path, string_types): + path = [path] + else: + path = list(path) + mod.__path__ = path + + sys.modules[package_name] = weakref.proxy(mod, + lambda x: sys.modules.pop(package_name, None)) + + # the only strong reference, the sys.modules entry is weak + # so that the garbage collector can remove it once the + # loader that created it goes out of business. + self.module = mod + self.package_name = package_name + + @staticmethod + def get_template_key(name): + return 'tmpl_' + sha1(name.encode('utf-8')).hexdigest() + + @staticmethod + def get_module_filename(name): + return ModuleLoader.get_template_key(name) + '.py' + + @internalcode + def load(self, environment, name, globals=None): + key = self.get_template_key(name) + module = '%s.%s' % (self.package_name, key) + mod = getattr(self.module, module, None) + if mod is None: + try: + mod = __import__(module, None, None, ['root']) + except ImportError: + raise TemplateNotFound(name) + + # remove the entry from sys.modules, we only want the attribute + # on the module object we have stored on the loader. + sys.modules.pop(module, None) + + return environment.template_class.from_module_dict( + environment, mod.__dict__, globals) diff --git a/mojo/public/third_party/jinja2/meta.py b/mojo/public/third_party/jinja2/meta.py new file mode 100644 index 0000000..3110cff --- /dev/null +++ b/mojo/public/third_party/jinja2/meta.py @@ -0,0 +1,103 @@ +# -*- coding: utf-8 -*- +""" + jinja2.meta + ~~~~~~~~~~~ + + This module implements various functions that exposes information about + templates that might be interesting for various kinds of applications. + + :copyright: (c) 2010 by the Jinja Team, see AUTHORS for more details. + :license: BSD, see LICENSE for more details. +""" +from jinja2 import nodes +from jinja2.compiler import CodeGenerator +from jinja2._compat import string_types + + +class TrackingCodeGenerator(CodeGenerator): + """We abuse the code generator for introspection.""" + + def __init__(self, environment): + CodeGenerator.__init__(self, environment, '<introspection>', + '<introspection>') + self.undeclared_identifiers = set() + + def write(self, x): + """Don't write.""" + + def pull_locals(self, frame): + """Remember all undeclared identifiers.""" + self.undeclared_identifiers.update(frame.identifiers.undeclared) + + +def find_undeclared_variables(ast): + """Returns a set of all variables in the AST that will be looked up from + the context at runtime. Because at compile time it's not known which + variables will be used depending on the path the execution takes at + runtime, all variables are returned. + + >>> from jinja2 import Environment, meta + >>> env = Environment() + >>> ast = env.parse('{% set foo = 42 %}{{ bar + foo }}') + >>> meta.find_undeclared_variables(ast) + set(['bar']) + + .. admonition:: Implementation + + Internally the code generator is used for finding undeclared variables. + This is good to know because the code generator might raise a + :exc:`TemplateAssertionError` during compilation and as a matter of + fact this function can currently raise that exception as well. + """ + codegen = TrackingCodeGenerator(ast.environment) + codegen.visit(ast) + return codegen.undeclared_identifiers + + +def find_referenced_templates(ast): + """Finds all the referenced templates from the AST. This will return an + iterator over all the hardcoded template extensions, inclusions and + imports. If dynamic inheritance or inclusion is used, `None` will be + yielded. + + >>> from jinja2 import Environment, meta + >>> env = Environment() + >>> ast = env.parse('{% extends "layout.html" %}{% include helper %}') + >>> list(meta.find_referenced_templates(ast)) + ['layout.html', None] + + This function is useful for dependency tracking. For example if you want + to rebuild parts of the website after a layout template has changed. + """ + for node in ast.find_all((nodes.Extends, nodes.FromImport, nodes.Import, + nodes.Include)): + if not isinstance(node.template, nodes.Const): + # a tuple with some non consts in there + if isinstance(node.template, (nodes.Tuple, nodes.List)): + for template_name in node.template.items: + # something const, only yield the strings and ignore + # non-string consts that really just make no sense + if isinstance(template_name, nodes.Const): + if isinstance(template_name.value, string_types): + yield template_name.value + # something dynamic in there + else: + yield None + # something dynamic we don't know about here + else: + yield None + continue + # constant is a basestring, direct template name + if isinstance(node.template.value, string_types): + yield node.template.value + # a tuple or list (latter *should* not happen) made of consts, + # yield the consts that are strings. We could warn here for + # non string values + elif isinstance(node, nodes.Include) and \ + isinstance(node.template.value, (tuple, list)): + for template_name in node.template.value: + if isinstance(template_name, string_types): + yield template_name + # something else we don't care about, we could warn here + else: + yield None diff --git a/mojo/public/third_party/jinja2/nodes.py b/mojo/public/third_party/jinja2/nodes.py new file mode 100644 index 0000000..c5697e6 --- /dev/null +++ b/mojo/public/third_party/jinja2/nodes.py @@ -0,0 +1,914 @@ +# -*- coding: utf-8 -*- +""" + jinja2.nodes + ~~~~~~~~~~~~ + + This module implements additional nodes derived from the ast base node. + + It also provides some node tree helper functions like `in_lineno` and + `get_nodes` used by the parser and translator in order to normalize + python and jinja nodes. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import operator + +from collections import deque +from jinja2.utils import Markup +from jinja2._compat import next, izip, with_metaclass, text_type, \ + method_type, function_type + + +#: the types we support for context functions +_context_function_types = (function_type, method_type) + + +_binop_to_func = { + '*': operator.mul, + '/': operator.truediv, + '//': operator.floordiv, + '**': operator.pow, + '%': operator.mod, + '+': operator.add, + '-': operator.sub +} + +_uaop_to_func = { + 'not': operator.not_, + '+': operator.pos, + '-': operator.neg +} + +_cmpop_to_func = { + 'eq': operator.eq, + 'ne': operator.ne, + 'gt': operator.gt, + 'gteq': operator.ge, + 'lt': operator.lt, + 'lteq': operator.le, + 'in': lambda a, b: a in b, + 'notin': lambda a, b: a not in b +} + + +class Impossible(Exception): + """Raised if the node could not perform a requested action.""" + + +class NodeType(type): + """A metaclass for nodes that handles the field and attribute + inheritance. fields and attributes from the parent class are + automatically forwarded to the child.""" + + def __new__(cls, name, bases, d): + for attr in 'fields', 'attributes': + storage = [] + storage.extend(getattr(bases[0], attr, ())) + storage.extend(d.get(attr, ())) + assert len(bases) == 1, 'multiple inheritance not allowed' + assert len(storage) == len(set(storage)), 'layout conflict' + d[attr] = tuple(storage) + d.setdefault('abstract', False) + return type.__new__(cls, name, bases, d) + + +class EvalContext(object): + """Holds evaluation time information. Custom attributes can be attached + to it in extensions. + """ + + def __init__(self, environment, template_name=None): + self.environment = environment + if callable(environment.autoescape): + self.autoescape = environment.autoescape(template_name) + else: + self.autoescape = environment.autoescape + self.volatile = False + + def save(self): + return self.__dict__.copy() + + def revert(self, old): + self.__dict__.clear() + self.__dict__.update(old) + + +def get_eval_context(node, ctx): + if ctx is None: + if node.environment is None: + raise RuntimeError('if no eval context is passed, the ' + 'node must have an attached ' + 'environment.') + return EvalContext(node.environment) + return ctx + + +class Node(with_metaclass(NodeType, object)): + """Baseclass for all Jinja2 nodes. There are a number of nodes available + of different types. There are four major types: + + - :class:`Stmt`: statements + - :class:`Expr`: expressions + - :class:`Helper`: helper nodes + - :class:`Template`: the outermost wrapper node + + All nodes have fields and attributes. Fields may be other nodes, lists, + or arbitrary values. Fields are passed to the constructor as regular + positional arguments, attributes as keyword arguments. Each node has + two attributes: `lineno` (the line number of the node) and `environment`. + The `environment` attribute is set at the end of the parsing process for + all nodes automatically. + """ + fields = () + attributes = ('lineno', 'environment') + abstract = True + + def __init__(self, *fields, **attributes): + if self.abstract: + raise TypeError('abstract nodes are not instanciable') + if fields: + if len(fields) != len(self.fields): + if not self.fields: + raise TypeError('%r takes 0 arguments' % + self.__class__.__name__) + raise TypeError('%r takes 0 or %d argument%s' % ( + self.__class__.__name__, + len(self.fields), + len(self.fields) != 1 and 's' or '' + )) + for name, arg in izip(self.fields, fields): + setattr(self, name, arg) + for attr in self.attributes: + setattr(self, attr, attributes.pop(attr, None)) + if attributes: + raise TypeError('unknown attribute %r' % + next(iter(attributes))) + + def iter_fields(self, exclude=None, only=None): + """This method iterates over all fields that are defined and yields + ``(key, value)`` tuples. Per default all fields are returned, but + it's possible to limit that to some fields by providing the `only` + parameter or to exclude some using the `exclude` parameter. Both + should be sets or tuples of field names. + """ + for name in self.fields: + if (exclude is only is None) or \ + (exclude is not None and name not in exclude) or \ + (only is not None and name in only): + try: + yield name, getattr(self, name) + except AttributeError: + pass + + def iter_child_nodes(self, exclude=None, only=None): + """Iterates over all direct child nodes of the node. This iterates + over all fields and yields the values of they are nodes. If the value + of a field is a list all the nodes in that list are returned. + """ + for field, item in self.iter_fields(exclude, only): + if isinstance(item, list): + for n in item: + if isinstance(n, Node): + yield n + elif isinstance(item, Node): + yield item + + def find(self, node_type): + """Find the first node of a given type. If no such node exists the + return value is `None`. + """ + for result in self.find_all(node_type): + return result + + def find_all(self, node_type): + """Find all the nodes of a given type. If the type is a tuple, + the check is performed for any of the tuple items. + """ + for child in self.iter_child_nodes(): + if isinstance(child, node_type): + yield child + for result in child.find_all(node_type): + yield result + + def set_ctx(self, ctx): + """Reset the context of a node and all child nodes. Per default the + parser will all generate nodes that have a 'load' context as it's the + most common one. This method is used in the parser to set assignment + targets and other nodes to a store context. + """ + todo = deque([self]) + while todo: + node = todo.popleft() + if 'ctx' in node.fields: + node.ctx = ctx + todo.extend(node.iter_child_nodes()) + return self + + def set_lineno(self, lineno, override=False): + """Set the line numbers of the node and children.""" + todo = deque([self]) + while todo: + node = todo.popleft() + if 'lineno' in node.attributes: + if node.lineno is None or override: + node.lineno = lineno + todo.extend(node.iter_child_nodes()) + return self + + def set_environment(self, environment): + """Set the environment for all nodes.""" + todo = deque([self]) + while todo: + node = todo.popleft() + node.environment = environment + todo.extend(node.iter_child_nodes()) + return self + + def __eq__(self, other): + return type(self) is type(other) and \ + tuple(self.iter_fields()) == tuple(other.iter_fields()) + + def __ne__(self, other): + return not self.__eq__(other) + + # Restore Python 2 hashing behavior on Python 3 + __hash__ = object.__hash__ + + def __repr__(self): + return '%s(%s)' % ( + self.__class__.__name__, + ', '.join('%s=%r' % (arg, getattr(self, arg, None)) for + arg in self.fields) + ) + + +class Stmt(Node): + """Base node for all statements.""" + abstract = True + + +class Helper(Node): + """Nodes that exist in a specific context only.""" + abstract = True + + +class Template(Node): + """Node that represents a template. This must be the outermost node that + is passed to the compiler. + """ + fields = ('body',) + + +class Output(Stmt): + """A node that holds multiple expressions which are then printed out. + This is used both for the `print` statement and the regular template data. + """ + fields = ('nodes',) + + +class Extends(Stmt): + """Represents an extends statement.""" + fields = ('template',) + + +class For(Stmt): + """The for loop. `target` is the target for the iteration (usually a + :class:`Name` or :class:`Tuple`), `iter` the iterable. `body` is a list + of nodes that are used as loop-body, and `else_` a list of nodes for the + `else` block. If no else node exists it has to be an empty list. + + For filtered nodes an expression can be stored as `test`, otherwise `None`. + """ + fields = ('target', 'iter', 'body', 'else_', 'test', 'recursive') + + +class If(Stmt): + """If `test` is true, `body` is rendered, else `else_`.""" + fields = ('test', 'body', 'else_') + + +class Macro(Stmt): + """A macro definition. `name` is the name of the macro, `args` a list of + arguments and `defaults` a list of defaults if there are any. `body` is + a list of nodes for the macro body. + """ + fields = ('name', 'args', 'defaults', 'body') + + +class CallBlock(Stmt): + """Like a macro without a name but a call instead. `call` is called with + the unnamed macro as `caller` argument this node holds. + """ + fields = ('call', 'args', 'defaults', 'body') + + +class FilterBlock(Stmt): + """Node for filter sections.""" + fields = ('body', 'filter') + + +class Block(Stmt): + """A node that represents a block.""" + fields = ('name', 'body', 'scoped') + + +class Include(Stmt): + """A node that represents the include tag.""" + fields = ('template', 'with_context', 'ignore_missing') + + +class Import(Stmt): + """A node that represents the import tag.""" + fields = ('template', 'target', 'with_context') + + +class FromImport(Stmt): + """A node that represents the from import tag. It's important to not + pass unsafe names to the name attribute. The compiler translates the + attribute lookups directly into getattr calls and does *not* use the + subscript callback of the interface. As exported variables may not + start with double underscores (which the parser asserts) this is not a + problem for regular Jinja code, but if this node is used in an extension + extra care must be taken. + + The list of names may contain tuples if aliases are wanted. + """ + fields = ('template', 'names', 'with_context') + + +class ExprStmt(Stmt): + """A statement that evaluates an expression and discards the result.""" + fields = ('node',) + + +class Assign(Stmt): + """Assigns an expression to a target.""" + fields = ('target', 'node') + + +class Expr(Node): + """Baseclass for all expressions.""" + abstract = True + + def as_const(self, eval_ctx=None): + """Return the value of the expression as constant or raise + :exc:`Impossible` if this was not possible. + + An :class:`EvalContext` can be provided, if none is given + a default context is created which requires the nodes to have + an attached environment. + + .. versionchanged:: 2.4 + the `eval_ctx` parameter was added. + """ + raise Impossible() + + def can_assign(self): + """Check if it's possible to assign something to this node.""" + return False + + +class BinExpr(Expr): + """Baseclass for all binary expressions.""" + fields = ('left', 'right') + operator = None + abstract = True + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + # intercepted operators cannot be folded at compile time + if self.environment.sandboxed and \ + self.operator in self.environment.intercepted_binops: + raise Impossible() + f = _binop_to_func[self.operator] + try: + return f(self.left.as_const(eval_ctx), self.right.as_const(eval_ctx)) + except Exception: + raise Impossible() + + +class UnaryExpr(Expr): + """Baseclass for all unary expressions.""" + fields = ('node',) + operator = None + abstract = True + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + # intercepted operators cannot be folded at compile time + if self.environment.sandboxed and \ + self.operator in self.environment.intercepted_unops: + raise Impossible() + f = _uaop_to_func[self.operator] + try: + return f(self.node.as_const(eval_ctx)) + except Exception: + raise Impossible() + + +class Name(Expr): + """Looks up a name or stores a value in a name. + The `ctx` of the node can be one of the following values: + + - `store`: store a value in the name + - `load`: load that name + - `param`: like `store` but if the name was defined as function parameter. + """ + fields = ('name', 'ctx') + + def can_assign(self): + return self.name not in ('true', 'false', 'none', + 'True', 'False', 'None') + + +class Literal(Expr): + """Baseclass for literals.""" + abstract = True + + +class Const(Literal): + """All constant values. The parser will return this node for simple + constants such as ``42`` or ``"foo"`` but it can be used to store more + complex values such as lists too. Only constants with a safe + representation (objects where ``eval(repr(x)) == x`` is true). + """ + fields = ('value',) + + def as_const(self, eval_ctx=None): + return self.value + + @classmethod + def from_untrusted(cls, value, lineno=None, environment=None): + """Return a const object if the value is representable as + constant value in the generated code, otherwise it will raise + an `Impossible` exception. + """ + from .compiler import has_safe_repr + if not has_safe_repr(value): + raise Impossible() + return cls(value, lineno=lineno, environment=environment) + + +class TemplateData(Literal): + """A constant template string.""" + fields = ('data',) + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + if eval_ctx.volatile: + raise Impossible() + if eval_ctx.autoescape: + return Markup(self.data) + return self.data + + +class Tuple(Literal): + """For loop unpacking and some other things like multiple arguments + for subscripts. Like for :class:`Name` `ctx` specifies if the tuple + is used for loading the names or storing. + """ + fields = ('items', 'ctx') + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return tuple(x.as_const(eval_ctx) for x in self.items) + + def can_assign(self): + for item in self.items: + if not item.can_assign(): + return False + return True + + +class List(Literal): + """Any list literal such as ``[1, 2, 3]``""" + fields = ('items',) + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return [x.as_const(eval_ctx) for x in self.items] + + +class Dict(Literal): + """Any dict literal such as ``{1: 2, 3: 4}``. The items must be a list of + :class:`Pair` nodes. + """ + fields = ('items',) + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return dict(x.as_const(eval_ctx) for x in self.items) + + +class Pair(Helper): + """A key, value pair for dicts.""" + fields = ('key', 'value') + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return self.key.as_const(eval_ctx), self.value.as_const(eval_ctx) + + +class Keyword(Helper): + """A key, value pair for keyword arguments where key is a string.""" + fields = ('key', 'value') + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return self.key, self.value.as_const(eval_ctx) + + +class CondExpr(Expr): + """A conditional expression (inline if expression). (``{{ + foo if bar else baz }}``) + """ + fields = ('test', 'expr1', 'expr2') + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + if self.test.as_const(eval_ctx): + return self.expr1.as_const(eval_ctx) + + # if we evaluate to an undefined object, we better do that at runtime + if self.expr2 is None: + raise Impossible() + + return self.expr2.as_const(eval_ctx) + + +class Filter(Expr): + """This node applies a filter on an expression. `name` is the name of + the filter, the rest of the fields are the same as for :class:`Call`. + + If the `node` of a filter is `None` the contents of the last buffer are + filtered. Buffers are created by macros and filter blocks. + """ + fields = ('node', 'name', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs') + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + if eval_ctx.volatile or self.node is None: + raise Impossible() + # we have to be careful here because we call filter_ below. + # if this variable would be called filter, 2to3 would wrap the + # call in a list beause it is assuming we are talking about the + # builtin filter function here which no longer returns a list in + # python 3. because of that, do not rename filter_ to filter! + filter_ = self.environment.filters.get(self.name) + if filter_ is None or getattr(filter_, 'contextfilter', False): + raise Impossible() + obj = self.node.as_const(eval_ctx) + args = [x.as_const(eval_ctx) for x in self.args] + if getattr(filter_, 'evalcontextfilter', False): + args.insert(0, eval_ctx) + elif getattr(filter_, 'environmentfilter', False): + args.insert(0, self.environment) + kwargs = dict(x.as_const(eval_ctx) for x in self.kwargs) + if self.dyn_args is not None: + try: + args.extend(self.dyn_args.as_const(eval_ctx)) + except Exception: + raise Impossible() + if self.dyn_kwargs is not None: + try: + kwargs.update(self.dyn_kwargs.as_const(eval_ctx)) + except Exception: + raise Impossible() + try: + return filter_(obj, *args, **kwargs) + except Exception: + raise Impossible() + + +class Test(Expr): + """Applies a test on an expression. `name` is the name of the test, the + rest of the fields are the same as for :class:`Call`. + """ + fields = ('node', 'name', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs') + + +class Call(Expr): + """Calls an expression. `args` is a list of arguments, `kwargs` a list + of keyword arguments (list of :class:`Keyword` nodes), and `dyn_args` + and `dyn_kwargs` has to be either `None` or a node that is used as + node for dynamic positional (``*args``) or keyword (``**kwargs``) + arguments. + """ + fields = ('node', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs') + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + if eval_ctx.volatile: + raise Impossible() + obj = self.node.as_const(eval_ctx) + + # don't evaluate context functions + args = [x.as_const(eval_ctx) for x in self.args] + if isinstance(obj, _context_function_types): + if getattr(obj, 'contextfunction', False): + raise Impossible() + elif getattr(obj, 'evalcontextfunction', False): + args.insert(0, eval_ctx) + elif getattr(obj, 'environmentfunction', False): + args.insert(0, self.environment) + + kwargs = dict(x.as_const(eval_ctx) for x in self.kwargs) + if self.dyn_args is not None: + try: + args.extend(self.dyn_args.as_const(eval_ctx)) + except Exception: + raise Impossible() + if self.dyn_kwargs is not None: + try: + kwargs.update(self.dyn_kwargs.as_const(eval_ctx)) + except Exception: + raise Impossible() + try: + return obj(*args, **kwargs) + except Exception: + raise Impossible() + + +class Getitem(Expr): + """Get an attribute or item from an expression and prefer the item.""" + fields = ('node', 'arg', 'ctx') + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + if self.ctx != 'load': + raise Impossible() + try: + return self.environment.getitem(self.node.as_const(eval_ctx), + self.arg.as_const(eval_ctx)) + except Exception: + raise Impossible() + + def can_assign(self): + return False + + +class Getattr(Expr): + """Get an attribute or item from an expression that is a ascii-only + bytestring and prefer the attribute. + """ + fields = ('node', 'attr', 'ctx') + + def as_const(self, eval_ctx=None): + if self.ctx != 'load': + raise Impossible() + try: + eval_ctx = get_eval_context(self, eval_ctx) + return self.environment.getattr(self.node.as_const(eval_ctx), + self.attr) + except Exception: + raise Impossible() + + def can_assign(self): + return False + + +class Slice(Expr): + """Represents a slice object. This must only be used as argument for + :class:`Subscript`. + """ + fields = ('start', 'stop', 'step') + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + def const(obj): + if obj is None: + return None + return obj.as_const(eval_ctx) + return slice(const(self.start), const(self.stop), const(self.step)) + + +class Concat(Expr): + """Concatenates the list of expressions provided after converting them to + unicode. + """ + fields = ('nodes',) + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return ''.join(text_type(x.as_const(eval_ctx)) for x in self.nodes) + + +class Compare(Expr): + """Compares an expression with some other expressions. `ops` must be a + list of :class:`Operand`\s. + """ + fields = ('expr', 'ops') + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + result = value = self.expr.as_const(eval_ctx) + try: + for op in self.ops: + new_value = op.expr.as_const(eval_ctx) + result = _cmpop_to_func[op.op](value, new_value) + value = new_value + except Exception: + raise Impossible() + return result + + +class Operand(Helper): + """Holds an operator and an expression.""" + fields = ('op', 'expr') + +if __debug__: + Operand.__doc__ += '\nThe following operators are available: ' + \ + ', '.join(sorted('``%s``' % x for x in set(_binop_to_func) | + set(_uaop_to_func) | set(_cmpop_to_func))) + + +class Mul(BinExpr): + """Multiplies the left with the right node.""" + operator = '*' + + +class Div(BinExpr): + """Divides the left by the right node.""" + operator = '/' + + +class FloorDiv(BinExpr): + """Divides the left by the right node and truncates conver the + result into an integer by truncating. + """ + operator = '//' + + +class Add(BinExpr): + """Add the left to the right node.""" + operator = '+' + + +class Sub(BinExpr): + """Substract the right from the left node.""" + operator = '-' + + +class Mod(BinExpr): + """Left modulo right.""" + operator = '%' + + +class Pow(BinExpr): + """Left to the power of right.""" + operator = '**' + + +class And(BinExpr): + """Short circuited AND.""" + operator = 'and' + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return self.left.as_const(eval_ctx) and self.right.as_const(eval_ctx) + + +class Or(BinExpr): + """Short circuited OR.""" + operator = 'or' + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return self.left.as_const(eval_ctx) or self.right.as_const(eval_ctx) + + +class Not(UnaryExpr): + """Negate the expression.""" + operator = 'not' + + +class Neg(UnaryExpr): + """Make the expression negative.""" + operator = '-' + + +class Pos(UnaryExpr): + """Make the expression positive (noop for most expressions)""" + operator = '+' + + +# Helpers for extensions + + +class EnvironmentAttribute(Expr): + """Loads an attribute from the environment object. This is useful for + extensions that want to call a callback stored on the environment. + """ + fields = ('name',) + + +class ExtensionAttribute(Expr): + """Returns the attribute of an extension bound to the environment. + The identifier is the identifier of the :class:`Extension`. + + This node is usually constructed by calling the + :meth:`~jinja2.ext.Extension.attr` method on an extension. + """ + fields = ('identifier', 'name') + + +class ImportedName(Expr): + """If created with an import name the import name is returned on node + access. For example ``ImportedName('cgi.escape')`` returns the `escape` + function from the cgi module on evaluation. Imports are optimized by the + compiler so there is no need to assign them to local variables. + """ + fields = ('importname',) + + +class InternalName(Expr): + """An internal name in the compiler. You cannot create these nodes + yourself but the parser provides a + :meth:`~jinja2.parser.Parser.free_identifier` method that creates + a new identifier for you. This identifier is not available from the + template and is not threated specially by the compiler. + """ + fields = ('name',) + + def __init__(self): + raise TypeError('Can\'t create internal names. Use the ' + '`free_identifier` method on a parser.') + + +class MarkSafe(Expr): + """Mark the wrapped expression as safe (wrap it as `Markup`).""" + fields = ('expr',) + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return Markup(self.expr.as_const(eval_ctx)) + + +class MarkSafeIfAutoescape(Expr): + """Mark the wrapped expression as safe (wrap it as `Markup`) but + only if autoescaping is active. + + .. versionadded:: 2.5 + """ + fields = ('expr',) + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + if eval_ctx.volatile: + raise Impossible() + expr = self.expr.as_const(eval_ctx) + if eval_ctx.autoescape: + return Markup(expr) + return expr + + +class ContextReference(Expr): + """Returns the current template context. It can be used like a + :class:`Name` node, with a ``'load'`` ctx and will return the + current :class:`~jinja2.runtime.Context` object. + + Here an example that assigns the current template name to a + variable named `foo`:: + + Assign(Name('foo', ctx='store'), + Getattr(ContextReference(), 'name')) + """ + + +class Continue(Stmt): + """Continue a loop.""" + + +class Break(Stmt): + """Break a loop.""" + + +class Scope(Stmt): + """An artificial scope.""" + fields = ('body',) + + +class EvalContextModifier(Stmt): + """Modifies the eval context. For each option that should be modified, + a :class:`Keyword` has to be added to the :attr:`options` list. + + Example to change the `autoescape` setting:: + + EvalContextModifier(options=[Keyword('autoescape', Const(True))]) + """ + fields = ('options',) + + +class ScopedEvalContextModifier(EvalContextModifier): + """Modifies the eval context and reverts it later. Works exactly like + :class:`EvalContextModifier` but will only modify the + :class:`~jinja2.nodes.EvalContext` for nodes in the :attr:`body`. + """ + fields = ('body',) + + +# make sure nobody creates custom nodes +def _failing_new(*args, **kwargs): + raise TypeError('can\'t create custom node types') +NodeType.__new__ = staticmethod(_failing_new); del _failing_new diff --git a/mojo/public/third_party/jinja2/optimizer.py b/mojo/public/third_party/jinja2/optimizer.py new file mode 100644 index 0000000..00eab11 --- /dev/null +++ b/mojo/public/third_party/jinja2/optimizer.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +""" + jinja2.optimizer + ~~~~~~~~~~~~~~~~ + + The jinja optimizer is currently trying to constant fold a few expressions + and modify the AST in place so that it should be easier to evaluate it. + + Because the AST does not contain all the scoping information and the + compiler has to find that out, we cannot do all the optimizations we + want. For example loop unrolling doesn't work because unrolled loops would + have a different scoping. + + The solution would be a second syntax tree that has the scoping rules stored. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD. +""" +from jinja2 import nodes +from jinja2.visitor import NodeTransformer + + +def optimize(node, environment): + """The context hint can be used to perform an static optimization + based on the context given.""" + optimizer = Optimizer(environment) + return optimizer.visit(node) + + +class Optimizer(NodeTransformer): + + def __init__(self, environment): + self.environment = environment + + def visit_If(self, node): + """Eliminate dead code.""" + # do not optimize ifs that have a block inside so that it doesn't + # break super(). + if node.find(nodes.Block) is not None: + return self.generic_visit(node) + try: + val = self.visit(node.test).as_const() + except nodes.Impossible: + return self.generic_visit(node) + if val: + body = node.body + else: + body = node.else_ + result = [] + for node in body: + result.extend(self.visit_list(node)) + return result + + def fold(self, node): + """Do constant folding.""" + node = self.generic_visit(node) + try: + return nodes.Const.from_untrusted(node.as_const(), + lineno=node.lineno, + environment=self.environment) + except nodes.Impossible: + return node + + visit_Add = visit_Sub = visit_Mul = visit_Div = visit_FloorDiv = \ + visit_Pow = visit_Mod = visit_And = visit_Or = visit_Pos = visit_Neg = \ + visit_Not = visit_Compare = visit_Getitem = visit_Getattr = visit_Call = \ + visit_Filter = visit_Test = visit_CondExpr = fold + del fold diff --git a/mojo/public/third_party/jinja2/parser.py b/mojo/public/third_party/jinja2/parser.py new file mode 100644 index 0000000..f60cd01 --- /dev/null +++ b/mojo/public/third_party/jinja2/parser.py @@ -0,0 +1,895 @@ +# -*- coding: utf-8 -*- +""" + jinja2.parser + ~~~~~~~~~~~~~ + + Implements the template parser. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +from jinja2 import nodes +from jinja2.exceptions import TemplateSyntaxError, TemplateAssertionError +from jinja2.lexer import describe_token, describe_token_expr +from jinja2._compat import next, imap + + +#: statements that callinto +_statement_keywords = frozenset(['for', 'if', 'block', 'extends', 'print', + 'macro', 'include', 'from', 'import', + 'set']) +_compare_operators = frozenset(['eq', 'ne', 'lt', 'lteq', 'gt', 'gteq']) + + +class Parser(object): + """This is the central parsing class Jinja2 uses. It's passed to + extensions and can be used to parse expressions or statements. + """ + + def __init__(self, environment, source, name=None, filename=None, + state=None): + self.environment = environment + self.stream = environment._tokenize(source, name, filename, state) + self.name = name + self.filename = filename + self.closed = False + self.extensions = {} + for extension in environment.iter_extensions(): + for tag in extension.tags: + self.extensions[tag] = extension.parse + self._last_identifier = 0 + self._tag_stack = [] + self._end_token_stack = [] + + def fail(self, msg, lineno=None, exc=TemplateSyntaxError): + """Convenience method that raises `exc` with the message, passed + line number or last line number as well as the current name and + filename. + """ + if lineno is None: + lineno = self.stream.current.lineno + raise exc(msg, lineno, self.name, self.filename) + + def _fail_ut_eof(self, name, end_token_stack, lineno): + expected = [] + for exprs in end_token_stack: + expected.extend(imap(describe_token_expr, exprs)) + if end_token_stack: + currently_looking = ' or '.join( + "'%s'" % describe_token_expr(expr) + for expr in end_token_stack[-1]) + else: + currently_looking = None + + if name is None: + message = ['Unexpected end of template.'] + else: + message = ['Encountered unknown tag \'%s\'.' % name] + + if currently_looking: + if name is not None and name in expected: + message.append('You probably made a nesting mistake. Jinja ' + 'is expecting this tag, but currently looking ' + 'for %s.' % currently_looking) + else: + message.append('Jinja was looking for the following tags: ' + '%s.' % currently_looking) + + if self._tag_stack: + message.append('The innermost block that needs to be ' + 'closed is \'%s\'.' % self._tag_stack[-1]) + + self.fail(' '.join(message), lineno) + + def fail_unknown_tag(self, name, lineno=None): + """Called if the parser encounters an unknown tag. Tries to fail + with a human readable error message that could help to identify + the problem. + """ + return self._fail_ut_eof(name, self._end_token_stack, lineno) + + def fail_eof(self, end_tokens=None, lineno=None): + """Like fail_unknown_tag but for end of template situations.""" + stack = list(self._end_token_stack) + if end_tokens is not None: + stack.append(end_tokens) + return self._fail_ut_eof(None, stack, lineno) + + def is_tuple_end(self, extra_end_rules=None): + """Are we at the end of a tuple?""" + if self.stream.current.type in ('variable_end', 'block_end', 'rparen'): + return True + elif extra_end_rules is not None: + return self.stream.current.test_any(extra_end_rules) + return False + + def free_identifier(self, lineno=None): + """Return a new free identifier as :class:`~jinja2.nodes.InternalName`.""" + self._last_identifier += 1 + rv = object.__new__(nodes.InternalName) + nodes.Node.__init__(rv, 'fi%d' % self._last_identifier, lineno=lineno) + return rv + + def parse_statement(self): + """Parse a single statement.""" + token = self.stream.current + if token.type != 'name': + self.fail('tag name expected', token.lineno) + self._tag_stack.append(token.value) + pop_tag = True + try: + if token.value in _statement_keywords: + return getattr(self, 'parse_' + self.stream.current.value)() + if token.value == 'call': + return self.parse_call_block() + if token.value == 'filter': + return self.parse_filter_block() + ext = self.extensions.get(token.value) + if ext is not None: + return ext(self) + + # did not work out, remove the token we pushed by accident + # from the stack so that the unknown tag fail function can + # produce a proper error message. + self._tag_stack.pop() + pop_tag = False + self.fail_unknown_tag(token.value, token.lineno) + finally: + if pop_tag: + self._tag_stack.pop() + + def parse_statements(self, end_tokens, drop_needle=False): + """Parse multiple statements into a list until one of the end tokens + is reached. This is used to parse the body of statements as it also + parses template data if appropriate. The parser checks first if the + current token is a colon and skips it if there is one. Then it checks + for the block end and parses until if one of the `end_tokens` is + reached. Per default the active token in the stream at the end of + the call is the matched end token. If this is not wanted `drop_needle` + can be set to `True` and the end token is removed. + """ + # the first token may be a colon for python compatibility + self.stream.skip_if('colon') + + # in the future it would be possible to add whole code sections + # by adding some sort of end of statement token and parsing those here. + self.stream.expect('block_end') + result = self.subparse(end_tokens) + + # we reached the end of the template too early, the subparser + # does not check for this, so we do that now + if self.stream.current.type == 'eof': + self.fail_eof(end_tokens) + + if drop_needle: + next(self.stream) + return result + + def parse_set(self): + """Parse an assign statement.""" + lineno = next(self.stream).lineno + target = self.parse_assign_target() + self.stream.expect('assign') + expr = self.parse_tuple() + return nodes.Assign(target, expr, lineno=lineno) + + def parse_for(self): + """Parse a for loop.""" + lineno = self.stream.expect('name:for').lineno + target = self.parse_assign_target(extra_end_rules=('name:in',)) + self.stream.expect('name:in') + iter = self.parse_tuple(with_condexpr=False, + extra_end_rules=('name:recursive',)) + test = None + if self.stream.skip_if('name:if'): + test = self.parse_expression() + recursive = self.stream.skip_if('name:recursive') + body = self.parse_statements(('name:endfor', 'name:else')) + if next(self.stream).value == 'endfor': + else_ = [] + else: + else_ = self.parse_statements(('name:endfor',), drop_needle=True) + return nodes.For(target, iter, body, else_, test, + recursive, lineno=lineno) + + def parse_if(self): + """Parse an if construct.""" + node = result = nodes.If(lineno=self.stream.expect('name:if').lineno) + while 1: + node.test = self.parse_tuple(with_condexpr=False) + node.body = self.parse_statements(('name:elif', 'name:else', + 'name:endif')) + token = next(self.stream) + if token.test('name:elif'): + new_node = nodes.If(lineno=self.stream.current.lineno) + node.else_ = [new_node] + node = new_node + continue + elif token.test('name:else'): + node.else_ = self.parse_statements(('name:endif',), + drop_needle=True) + else: + node.else_ = [] + break + return result + + def parse_block(self): + node = nodes.Block(lineno=next(self.stream).lineno) + node.name = self.stream.expect('name').value + node.scoped = self.stream.skip_if('name:scoped') + + # common problem people encounter when switching from django + # to jinja. we do not support hyphens in block names, so let's + # raise a nicer error message in that case. + if self.stream.current.type == 'sub': + self.fail('Block names in Jinja have to be valid Python ' + 'identifiers and may not contain hyphens, use an ' + 'underscore instead.') + + node.body = self.parse_statements(('name:endblock',), drop_needle=True) + self.stream.skip_if('name:' + node.name) + return node + + def parse_extends(self): + node = nodes.Extends(lineno=next(self.stream).lineno) + node.template = self.parse_expression() + return node + + def parse_import_context(self, node, default): + if self.stream.current.test_any('name:with', 'name:without') and \ + self.stream.look().test('name:context'): + node.with_context = next(self.stream).value == 'with' + self.stream.skip() + else: + node.with_context = default + return node + + def parse_include(self): + node = nodes.Include(lineno=next(self.stream).lineno) + node.template = self.parse_expression() + if self.stream.current.test('name:ignore') and \ + self.stream.look().test('name:missing'): + node.ignore_missing = True + self.stream.skip(2) + else: + node.ignore_missing = False + return self.parse_import_context(node, True) + + def parse_import(self): + node = nodes.Import(lineno=next(self.stream).lineno) + node.template = self.parse_expression() + self.stream.expect('name:as') + node.target = self.parse_assign_target(name_only=True).name + return self.parse_import_context(node, False) + + def parse_from(self): + node = nodes.FromImport(lineno=next(self.stream).lineno) + node.template = self.parse_expression() + self.stream.expect('name:import') + node.names = [] + + def parse_context(): + if self.stream.current.value in ('with', 'without') and \ + self.stream.look().test('name:context'): + node.with_context = next(self.stream).value == 'with' + self.stream.skip() + return True + return False + + while 1: + if node.names: + self.stream.expect('comma') + if self.stream.current.type == 'name': + if parse_context(): + break + target = self.parse_assign_target(name_only=True) + if target.name.startswith('_'): + self.fail('names starting with an underline can not ' + 'be imported', target.lineno, + exc=TemplateAssertionError) + if self.stream.skip_if('name:as'): + alias = self.parse_assign_target(name_only=True) + node.names.append((target.name, alias.name)) + else: + node.names.append(target.name) + if parse_context() or self.stream.current.type != 'comma': + break + else: + break + if not hasattr(node, 'with_context'): + node.with_context = False + self.stream.skip_if('comma') + return node + + def parse_signature(self, node): + node.args = args = [] + node.defaults = defaults = [] + self.stream.expect('lparen') + while self.stream.current.type != 'rparen': + if args: + self.stream.expect('comma') + arg = self.parse_assign_target(name_only=True) + arg.set_ctx('param') + if self.stream.skip_if('assign'): + defaults.append(self.parse_expression()) + args.append(arg) + self.stream.expect('rparen') + + def parse_call_block(self): + node = nodes.CallBlock(lineno=next(self.stream).lineno) + if self.stream.current.type == 'lparen': + self.parse_signature(node) + else: + node.args = [] + node.defaults = [] + + node.call = self.parse_expression() + if not isinstance(node.call, nodes.Call): + self.fail('expected call', node.lineno) + node.body = self.parse_statements(('name:endcall',), drop_needle=True) + return node + + def parse_filter_block(self): + node = nodes.FilterBlock(lineno=next(self.stream).lineno) + node.filter = self.parse_filter(None, start_inline=True) + node.body = self.parse_statements(('name:endfilter',), + drop_needle=True) + return node + + def parse_macro(self): + node = nodes.Macro(lineno=next(self.stream).lineno) + node.name = self.parse_assign_target(name_only=True).name + self.parse_signature(node) + node.body = self.parse_statements(('name:endmacro',), + drop_needle=True) + return node + + def parse_print(self): + node = nodes.Output(lineno=next(self.stream).lineno) + node.nodes = [] + while self.stream.current.type != 'block_end': + if node.nodes: + self.stream.expect('comma') + node.nodes.append(self.parse_expression()) + return node + + def parse_assign_target(self, with_tuple=True, name_only=False, + extra_end_rules=None): + """Parse an assignment target. As Jinja2 allows assignments to + tuples, this function can parse all allowed assignment targets. Per + default assignments to tuples are parsed, that can be disable however + by setting `with_tuple` to `False`. If only assignments to names are + wanted `name_only` can be set to `True`. The `extra_end_rules` + parameter is forwarded to the tuple parsing function. + """ + if name_only: + token = self.stream.expect('name') + target = nodes.Name(token.value, 'store', lineno=token.lineno) + else: + if with_tuple: + target = self.parse_tuple(simplified=True, + extra_end_rules=extra_end_rules) + else: + target = self.parse_primary() + target.set_ctx('store') + if not target.can_assign(): + self.fail('can\'t assign to %r' % target.__class__. + __name__.lower(), target.lineno) + return target + + def parse_expression(self, with_condexpr=True): + """Parse an expression. Per default all expressions are parsed, if + the optional `with_condexpr` parameter is set to `False` conditional + expressions are not parsed. + """ + if with_condexpr: + return self.parse_condexpr() + return self.parse_or() + + def parse_condexpr(self): + lineno = self.stream.current.lineno + expr1 = self.parse_or() + while self.stream.skip_if('name:if'): + expr2 = self.parse_or() + if self.stream.skip_if('name:else'): + expr3 = self.parse_condexpr() + else: + expr3 = None + expr1 = nodes.CondExpr(expr2, expr1, expr3, lineno=lineno) + lineno = self.stream.current.lineno + return expr1 + + def parse_or(self): + lineno = self.stream.current.lineno + left = self.parse_and() + while self.stream.skip_if('name:or'): + right = self.parse_and() + left = nodes.Or(left, right, lineno=lineno) + lineno = self.stream.current.lineno + return left + + def parse_and(self): + lineno = self.stream.current.lineno + left = self.parse_not() + while self.stream.skip_if('name:and'): + right = self.parse_not() + left = nodes.And(left, right, lineno=lineno) + lineno = self.stream.current.lineno + return left + + def parse_not(self): + if self.stream.current.test('name:not'): + lineno = next(self.stream).lineno + return nodes.Not(self.parse_not(), lineno=lineno) + return self.parse_compare() + + def parse_compare(self): + lineno = self.stream.current.lineno + expr = self.parse_add() + ops = [] + while 1: + token_type = self.stream.current.type + if token_type in _compare_operators: + next(self.stream) + ops.append(nodes.Operand(token_type, self.parse_add())) + elif self.stream.skip_if('name:in'): + ops.append(nodes.Operand('in', self.parse_add())) + elif self.stream.current.test('name:not') and \ + self.stream.look().test('name:in'): + self.stream.skip(2) + ops.append(nodes.Operand('notin', self.parse_add())) + else: + break + lineno = self.stream.current.lineno + if not ops: + return expr + return nodes.Compare(expr, ops, lineno=lineno) + + def parse_add(self): + lineno = self.stream.current.lineno + left = self.parse_sub() + while self.stream.current.type == 'add': + next(self.stream) + right = self.parse_sub() + left = nodes.Add(left, right, lineno=lineno) + lineno = self.stream.current.lineno + return left + + def parse_sub(self): + lineno = self.stream.current.lineno + left = self.parse_concat() + while self.stream.current.type == 'sub': + next(self.stream) + right = self.parse_concat() + left = nodes.Sub(left, right, lineno=lineno) + lineno = self.stream.current.lineno + return left + + def parse_concat(self): + lineno = self.stream.current.lineno + args = [self.parse_mul()] + while self.stream.current.type == 'tilde': + next(self.stream) + args.append(self.parse_mul()) + if len(args) == 1: + return args[0] + return nodes.Concat(args, lineno=lineno) + + def parse_mul(self): + lineno = self.stream.current.lineno + left = self.parse_div() + while self.stream.current.type == 'mul': + next(self.stream) + right = self.parse_div() + left = nodes.Mul(left, right, lineno=lineno) + lineno = self.stream.current.lineno + return left + + def parse_div(self): + lineno = self.stream.current.lineno + left = self.parse_floordiv() + while self.stream.current.type == 'div': + next(self.stream) + right = self.parse_floordiv() + left = nodes.Div(left, right, lineno=lineno) + lineno = self.stream.current.lineno + return left + + def parse_floordiv(self): + lineno = self.stream.current.lineno + left = self.parse_mod() + while self.stream.current.type == 'floordiv': + next(self.stream) + right = self.parse_mod() + left = nodes.FloorDiv(left, right, lineno=lineno) + lineno = self.stream.current.lineno + return left + + def parse_mod(self): + lineno = self.stream.current.lineno + left = self.parse_pow() + while self.stream.current.type == 'mod': + next(self.stream) + right = self.parse_pow() + left = nodes.Mod(left, right, lineno=lineno) + lineno = self.stream.current.lineno + return left + + def parse_pow(self): + lineno = self.stream.current.lineno + left = self.parse_unary() + while self.stream.current.type == 'pow': + next(self.stream) + right = self.parse_unary() + left = nodes.Pow(left, right, lineno=lineno) + lineno = self.stream.current.lineno + return left + + def parse_unary(self, with_filter=True): + token_type = self.stream.current.type + lineno = self.stream.current.lineno + if token_type == 'sub': + next(self.stream) + node = nodes.Neg(self.parse_unary(False), lineno=lineno) + elif token_type == 'add': + next(self.stream) + node = nodes.Pos(self.parse_unary(False), lineno=lineno) + else: + node = self.parse_primary() + node = self.parse_postfix(node) + if with_filter: + node = self.parse_filter_expr(node) + return node + + def parse_primary(self): + token = self.stream.current + if token.type == 'name': + if token.value in ('true', 'false', 'True', 'False'): + node = nodes.Const(token.value in ('true', 'True'), + lineno=token.lineno) + elif token.value in ('none', 'None'): + node = nodes.Const(None, lineno=token.lineno) + else: + node = nodes.Name(token.value, 'load', lineno=token.lineno) + next(self.stream) + elif token.type == 'string': + next(self.stream) + buf = [token.value] + lineno = token.lineno + while self.stream.current.type == 'string': + buf.append(self.stream.current.value) + next(self.stream) + node = nodes.Const(''.join(buf), lineno=lineno) + elif token.type in ('integer', 'float'): + next(self.stream) + node = nodes.Const(token.value, lineno=token.lineno) + elif token.type == 'lparen': + next(self.stream) + node = self.parse_tuple(explicit_parentheses=True) + self.stream.expect('rparen') + elif token.type == 'lbracket': + node = self.parse_list() + elif token.type == 'lbrace': + node = self.parse_dict() + else: + self.fail("unexpected '%s'" % describe_token(token), token.lineno) + return node + + def parse_tuple(self, simplified=False, with_condexpr=True, + extra_end_rules=None, explicit_parentheses=False): + """Works like `parse_expression` but if multiple expressions are + delimited by a comma a :class:`~jinja2.nodes.Tuple` node is created. + This method could also return a regular expression instead of a tuple + if no commas where found. + + The default parsing mode is a full tuple. If `simplified` is `True` + only names and literals are parsed. The `no_condexpr` parameter is + forwarded to :meth:`parse_expression`. + + Because tuples do not require delimiters and may end in a bogus comma + an extra hint is needed that marks the end of a tuple. For example + for loops support tuples between `for` and `in`. In that case the + `extra_end_rules` is set to ``['name:in']``. + + `explicit_parentheses` is true if the parsing was triggered by an + expression in parentheses. This is used to figure out if an empty + tuple is a valid expression or not. + """ + lineno = self.stream.current.lineno + if simplified: + parse = self.parse_primary + elif with_condexpr: + parse = self.parse_expression + else: + parse = lambda: self.parse_expression(with_condexpr=False) + args = [] + is_tuple = False + while 1: + if args: + self.stream.expect('comma') + if self.is_tuple_end(extra_end_rules): + break + args.append(parse()) + if self.stream.current.type == 'comma': + is_tuple = True + else: + break + lineno = self.stream.current.lineno + + if not is_tuple: + if args: + return args[0] + + # if we don't have explicit parentheses, an empty tuple is + # not a valid expression. This would mean nothing (literally + # nothing) in the spot of an expression would be an empty + # tuple. + if not explicit_parentheses: + self.fail('Expected an expression, got \'%s\'' % + describe_token(self.stream.current)) + + return nodes.Tuple(args, 'load', lineno=lineno) + + def parse_list(self): + token = self.stream.expect('lbracket') + items = [] + while self.stream.current.type != 'rbracket': + if items: + self.stream.expect('comma') + if self.stream.current.type == 'rbracket': + break + items.append(self.parse_expression()) + self.stream.expect('rbracket') + return nodes.List(items, lineno=token.lineno) + + def parse_dict(self): + token = self.stream.expect('lbrace') + items = [] + while self.stream.current.type != 'rbrace': + if items: + self.stream.expect('comma') + if self.stream.current.type == 'rbrace': + break + key = self.parse_expression() + self.stream.expect('colon') + value = self.parse_expression() + items.append(nodes.Pair(key, value, lineno=key.lineno)) + self.stream.expect('rbrace') + return nodes.Dict(items, lineno=token.lineno) + + def parse_postfix(self, node): + while 1: + token_type = self.stream.current.type + if token_type == 'dot' or token_type == 'lbracket': + node = self.parse_subscript(node) + # calls are valid both after postfix expressions (getattr + # and getitem) as well as filters and tests + elif token_type == 'lparen': + node = self.parse_call(node) + else: + break + return node + + def parse_filter_expr(self, node): + while 1: + token_type = self.stream.current.type + if token_type == 'pipe': + node = self.parse_filter(node) + elif token_type == 'name' and self.stream.current.value == 'is': + node = self.parse_test(node) + # calls are valid both after postfix expressions (getattr + # and getitem) as well as filters and tests + elif token_type == 'lparen': + node = self.parse_call(node) + else: + break + return node + + def parse_subscript(self, node): + token = next(self.stream) + if token.type == 'dot': + attr_token = self.stream.current + next(self.stream) + if attr_token.type == 'name': + return nodes.Getattr(node, attr_token.value, 'load', + lineno=token.lineno) + elif attr_token.type != 'integer': + self.fail('expected name or number', attr_token.lineno) + arg = nodes.Const(attr_token.value, lineno=attr_token.lineno) + return nodes.Getitem(node, arg, 'load', lineno=token.lineno) + if token.type == 'lbracket': + args = [] + while self.stream.current.type != 'rbracket': + if args: + self.stream.expect('comma') + args.append(self.parse_subscribed()) + self.stream.expect('rbracket') + if len(args) == 1: + arg = args[0] + else: + arg = nodes.Tuple(args, 'load', lineno=token.lineno) + return nodes.Getitem(node, arg, 'load', lineno=token.lineno) + self.fail('expected subscript expression', self.lineno) + + def parse_subscribed(self): + lineno = self.stream.current.lineno + + if self.stream.current.type == 'colon': + next(self.stream) + args = [None] + else: + node = self.parse_expression() + if self.stream.current.type != 'colon': + return node + next(self.stream) + args = [node] + + if self.stream.current.type == 'colon': + args.append(None) + elif self.stream.current.type not in ('rbracket', 'comma'): + args.append(self.parse_expression()) + else: + args.append(None) + + if self.stream.current.type == 'colon': + next(self.stream) + if self.stream.current.type not in ('rbracket', 'comma'): + args.append(self.parse_expression()) + else: + args.append(None) + else: + args.append(None) + + return nodes.Slice(lineno=lineno, *args) + + def parse_call(self, node): + token = self.stream.expect('lparen') + args = [] + kwargs = [] + dyn_args = dyn_kwargs = None + require_comma = False + + def ensure(expr): + if not expr: + self.fail('invalid syntax for function call expression', + token.lineno) + + while self.stream.current.type != 'rparen': + if require_comma: + self.stream.expect('comma') + # support for trailing comma + if self.stream.current.type == 'rparen': + break + if self.stream.current.type == 'mul': + ensure(dyn_args is None and dyn_kwargs is None) + next(self.stream) + dyn_args = self.parse_expression() + elif self.stream.current.type == 'pow': + ensure(dyn_kwargs is None) + next(self.stream) + dyn_kwargs = self.parse_expression() + else: + ensure(dyn_args is None and dyn_kwargs is None) + if self.stream.current.type == 'name' and \ + self.stream.look().type == 'assign': + key = self.stream.current.value + self.stream.skip(2) + value = self.parse_expression() + kwargs.append(nodes.Keyword(key, value, + lineno=value.lineno)) + else: + ensure(not kwargs) + args.append(self.parse_expression()) + + require_comma = True + self.stream.expect('rparen') + + if node is None: + return args, kwargs, dyn_args, dyn_kwargs + return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs, + lineno=token.lineno) + + def parse_filter(self, node, start_inline=False): + while self.stream.current.type == 'pipe' or start_inline: + if not start_inline: + next(self.stream) + token = self.stream.expect('name') + name = token.value + while self.stream.current.type == 'dot': + next(self.stream) + name += '.' + self.stream.expect('name').value + if self.stream.current.type == 'lparen': + args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None) + else: + args = [] + kwargs = [] + dyn_args = dyn_kwargs = None + node = nodes.Filter(node, name, args, kwargs, dyn_args, + dyn_kwargs, lineno=token.lineno) + start_inline = False + return node + + def parse_test(self, node): + token = next(self.stream) + if self.stream.current.test('name:not'): + next(self.stream) + negated = True + else: + negated = False + name = self.stream.expect('name').value + while self.stream.current.type == 'dot': + next(self.stream) + name += '.' + self.stream.expect('name').value + dyn_args = dyn_kwargs = None + kwargs = [] + if self.stream.current.type == 'lparen': + args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None) + elif self.stream.current.type in ('name', 'string', 'integer', + 'float', 'lparen', 'lbracket', + 'lbrace') and not \ + self.stream.current.test_any('name:else', 'name:or', + 'name:and'): + if self.stream.current.test('name:is'): + self.fail('You cannot chain multiple tests with is') + args = [self.parse_expression()] + else: + args = [] + node = nodes.Test(node, name, args, kwargs, dyn_args, + dyn_kwargs, lineno=token.lineno) + if negated: + node = nodes.Not(node, lineno=token.lineno) + return node + + def subparse(self, end_tokens=None): + body = [] + data_buffer = [] + add_data = data_buffer.append + + if end_tokens is not None: + self._end_token_stack.append(end_tokens) + + def flush_data(): + if data_buffer: + lineno = data_buffer[0].lineno + body.append(nodes.Output(data_buffer[:], lineno=lineno)) + del data_buffer[:] + + try: + while self.stream: + token = self.stream.current + if token.type == 'data': + if token.value: + add_data(nodes.TemplateData(token.value, + lineno=token.lineno)) + next(self.stream) + elif token.type == 'variable_begin': + next(self.stream) + add_data(self.parse_tuple(with_condexpr=True)) + self.stream.expect('variable_end') + elif token.type == 'block_begin': + flush_data() + next(self.stream) + if end_tokens is not None and \ + self.stream.current.test_any(*end_tokens): + return body + rv = self.parse_statement() + if isinstance(rv, list): + body.extend(rv) + else: + body.append(rv) + self.stream.expect('block_end') + else: + raise AssertionError('internal parsing error') + + flush_data() + finally: + if end_tokens is not None: + self._end_token_stack.pop() + + return body + + def parse(self): + """Parse the whole template into a `Template` node.""" + result = nodes.Template(self.subparse(), lineno=1) + result.set_environment(self.environment) + return result diff --git a/mojo/public/third_party/jinja2/runtime.py b/mojo/public/third_party/jinja2/runtime.py new file mode 100644 index 0000000..7791c64 --- /dev/null +++ b/mojo/public/third_party/jinja2/runtime.py @@ -0,0 +1,581 @@ +# -*- coding: utf-8 -*- +""" + jinja2.runtime + ~~~~~~~~~~~~~~ + + Runtime helpers. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD. +""" +from itertools import chain +from jinja2.nodes import EvalContext, _context_function_types +from jinja2.utils import Markup, soft_unicode, escape, missing, concat, \ + internalcode, object_type_repr +from jinja2.exceptions import UndefinedError, TemplateRuntimeError, \ + TemplateNotFound +from jinja2._compat import next, imap, text_type, iteritems, \ + implements_iterator, implements_to_string, string_types, PY2 + + +# these variables are exported to the template runtime +__all__ = ['LoopContext', 'TemplateReference', 'Macro', 'Markup', + 'TemplateRuntimeError', 'missing', 'concat', 'escape', + 'markup_join', 'unicode_join', 'to_string', 'identity', + 'TemplateNotFound'] + +#: the name of the function that is used to convert something into +#: a string. We can just use the text type here. +to_string = text_type + +#: the identity function. Useful for certain things in the environment +identity = lambda x: x + +_last_iteration = object() + + +def markup_join(seq): + """Concatenation that escapes if necessary and converts to unicode.""" + buf = [] + iterator = imap(soft_unicode, seq) + for arg in iterator: + buf.append(arg) + if hasattr(arg, '__html__'): + return Markup(u'').join(chain(buf, iterator)) + return concat(buf) + + +def unicode_join(seq): + """Simple args to unicode conversion and concatenation.""" + return concat(imap(text_type, seq)) + + +def new_context(environment, template_name, blocks, vars=None, + shared=None, globals=None, locals=None): + """Internal helper to for context creation.""" + if vars is None: + vars = {} + if shared: + parent = vars + else: + parent = dict(globals or (), **vars) + if locals: + # if the parent is shared a copy should be created because + # we don't want to modify the dict passed + if shared: + parent = dict(parent) + for key, value in iteritems(locals): + if key[:2] == 'l_' and value is not missing: + parent[key[2:]] = value + return Context(environment, parent, template_name, blocks) + + +class TemplateReference(object): + """The `self` in templates.""" + + def __init__(self, context): + self.__context = context + + def __getitem__(self, name): + blocks = self.__context.blocks[name] + return BlockReference(name, self.__context, blocks, 0) + + def __repr__(self): + return '<%s %r>' % ( + self.__class__.__name__, + self.__context.name + ) + + +class Context(object): + """The template context holds the variables of a template. It stores the + values passed to the template and also the names the template exports. + Creating instances is neither supported nor useful as it's created + automatically at various stages of the template evaluation and should not + be created by hand. + + The context is immutable. Modifications on :attr:`parent` **must not** + happen and modifications on :attr:`vars` are allowed from generated + template code only. Template filters and global functions marked as + :func:`contextfunction`\s get the active context passed as first argument + and are allowed to access the context read-only. + + The template context supports read only dict operations (`get`, + `keys`, `values`, `items`, `iterkeys`, `itervalues`, `iteritems`, + `__getitem__`, `__contains__`). Additionally there is a :meth:`resolve` + method that doesn't fail with a `KeyError` but returns an + :class:`Undefined` object for missing variables. + """ + __slots__ = ('parent', 'vars', 'environment', 'eval_ctx', 'exported_vars', + 'name', 'blocks', '__weakref__') + + def __init__(self, environment, parent, name, blocks): + self.parent = parent + self.vars = {} + self.environment = environment + self.eval_ctx = EvalContext(self.environment, name) + self.exported_vars = set() + self.name = name + + # create the initial mapping of blocks. Whenever template inheritance + # takes place the runtime will update this mapping with the new blocks + # from the template. + self.blocks = dict((k, [v]) for k, v in iteritems(blocks)) + + def super(self, name, current): + """Render a parent block.""" + try: + blocks = self.blocks[name] + index = blocks.index(current) + 1 + blocks[index] + except LookupError: + return self.environment.undefined('there is no parent block ' + 'called %r.' % name, + name='super') + return BlockReference(name, self, blocks, index) + + def get(self, key, default=None): + """Returns an item from the template context, if it doesn't exist + `default` is returned. + """ + try: + return self[key] + except KeyError: + return default + + def resolve(self, key): + """Looks up a variable like `__getitem__` or `get` but returns an + :class:`Undefined` object with the name of the name looked up. + """ + if key in self.vars: + return self.vars[key] + if key in self.parent: + return self.parent[key] + return self.environment.undefined(name=key) + + def get_exported(self): + """Get a new dict with the exported variables.""" + return dict((k, self.vars[k]) for k in self.exported_vars) + + def get_all(self): + """Return a copy of the complete context as dict including the + exported variables. + """ + return dict(self.parent, **self.vars) + + @internalcode + def call(__self, __obj, *args, **kwargs): + """Call the callable with the arguments and keyword arguments + provided but inject the active context or environment as first + argument if the callable is a :func:`contextfunction` or + :func:`environmentfunction`. + """ + if __debug__: + __traceback_hide__ = True + + # Allow callable classes to take a context + fn = __obj.__call__ + for fn_type in ('contextfunction', + 'evalcontextfunction', + 'environmentfunction'): + if hasattr(fn, fn_type): + __obj = fn + break + + if isinstance(__obj, _context_function_types): + if getattr(__obj, 'contextfunction', 0): + args = (__self,) + args + elif getattr(__obj, 'evalcontextfunction', 0): + args = (__self.eval_ctx,) + args + elif getattr(__obj, 'environmentfunction', 0): + args = (__self.environment,) + args + try: + return __obj(*args, **kwargs) + except StopIteration: + return __self.environment.undefined('value was undefined because ' + 'a callable raised a ' + 'StopIteration exception') + + def derived(self, locals=None): + """Internal helper function to create a derived context.""" + context = new_context(self.environment, self.name, {}, + self.parent, True, None, locals) + context.vars.update(self.vars) + context.eval_ctx = self.eval_ctx + context.blocks.update((k, list(v)) for k, v in iteritems(self.blocks)) + return context + + def _all(meth): + proxy = lambda self: getattr(self.get_all(), meth)() + proxy.__doc__ = getattr(dict, meth).__doc__ + proxy.__name__ = meth + return proxy + + keys = _all('keys') + values = _all('values') + items = _all('items') + + # not available on python 3 + if PY2: + iterkeys = _all('iterkeys') + itervalues = _all('itervalues') + iteritems = _all('iteritems') + del _all + + def __contains__(self, name): + return name in self.vars or name in self.parent + + def __getitem__(self, key): + """Lookup a variable or raise `KeyError` if the variable is + undefined. + """ + item = self.resolve(key) + if isinstance(item, Undefined): + raise KeyError(key) + return item + + def __repr__(self): + return '<%s %s of %r>' % ( + self.__class__.__name__, + repr(self.get_all()), + self.name + ) + + +# register the context as mapping if possible +try: + from collections import Mapping + Mapping.register(Context) +except ImportError: + pass + + +class BlockReference(object): + """One block on a template reference.""" + + def __init__(self, name, context, stack, depth): + self.name = name + self._context = context + self._stack = stack + self._depth = depth + + @property + def super(self): + """Super the block.""" + if self._depth + 1 >= len(self._stack): + return self._context.environment. \ + undefined('there is no parent block called %r.' % + self.name, name='super') + return BlockReference(self.name, self._context, self._stack, + self._depth + 1) + + @internalcode + def __call__(self): + rv = concat(self._stack[self._depth](self._context)) + if self._context.eval_ctx.autoescape: + rv = Markup(rv) + return rv + + +class LoopContext(object): + """A loop context for dynamic iteration.""" + + def __init__(self, iterable, recurse=None, depth0=0): + self._iterator = iter(iterable) + self._recurse = recurse + self._after = self._safe_next() + self.index0 = -1 + self.depth0 = depth0 + + # try to get the length of the iterable early. This must be done + # here because there are some broken iterators around where there + # __len__ is the number of iterations left (i'm looking at your + # listreverseiterator!). + try: + self._length = len(iterable) + except (TypeError, AttributeError): + self._length = None + + def cycle(self, *args): + """Cycles among the arguments with the current loop index.""" + if not args: + raise TypeError('no items for cycling given') + return args[self.index0 % len(args)] + + first = property(lambda x: x.index0 == 0) + last = property(lambda x: x._after is _last_iteration) + index = property(lambda x: x.index0 + 1) + revindex = property(lambda x: x.length - x.index0) + revindex0 = property(lambda x: x.length - x.index) + depth = property(lambda x: x.depth0 + 1) + + def __len__(self): + return self.length + + def __iter__(self): + return LoopContextIterator(self) + + def _safe_next(self): + try: + return next(self._iterator) + except StopIteration: + return _last_iteration + + @internalcode + def loop(self, iterable): + if self._recurse is None: + raise TypeError('Tried to call non recursive loop. Maybe you ' + "forgot the 'recursive' modifier.") + return self._recurse(iterable, self._recurse, self.depth0 + 1) + + # a nifty trick to enhance the error message if someone tried to call + # the the loop without or with too many arguments. + __call__ = loop + del loop + + @property + def length(self): + if self._length is None: + # if was not possible to get the length of the iterator when + # the loop context was created (ie: iterating over a generator) + # we have to convert the iterable into a sequence and use the + # length of that. + iterable = tuple(self._iterator) + self._iterator = iter(iterable) + self._length = len(iterable) + self.index0 + 1 + return self._length + + def __repr__(self): + return '<%s %r/%r>' % ( + self.__class__.__name__, + self.index, + self.length + ) + + +@implements_iterator +class LoopContextIterator(object): + """The iterator for a loop context.""" + __slots__ = ('context',) + + def __init__(self, context): + self.context = context + + def __iter__(self): + return self + + def __next__(self): + ctx = self.context + ctx.index0 += 1 + if ctx._after is _last_iteration: + raise StopIteration() + next_elem = ctx._after + ctx._after = ctx._safe_next() + return next_elem, ctx + + +class Macro(object): + """Wraps a macro function.""" + + def __init__(self, environment, func, name, arguments, defaults, + catch_kwargs, catch_varargs, caller): + self._environment = environment + self._func = func + self._argument_count = len(arguments) + self.name = name + self.arguments = arguments + self.defaults = defaults + self.catch_kwargs = catch_kwargs + self.catch_varargs = catch_varargs + self.caller = caller + + @internalcode + def __call__(self, *args, **kwargs): + # try to consume the positional arguments + arguments = list(args[:self._argument_count]) + off = len(arguments) + + # if the number of arguments consumed is not the number of + # arguments expected we start filling in keyword arguments + # and defaults. + if off != self._argument_count: + for idx, name in enumerate(self.arguments[len(arguments):]): + try: + value = kwargs.pop(name) + except KeyError: + try: + value = self.defaults[idx - self._argument_count + off] + except IndexError: + value = self._environment.undefined( + 'parameter %r was not provided' % name, name=name) + arguments.append(value) + + # it's important that the order of these arguments does not change + # if not also changed in the compiler's `function_scoping` method. + # the order is caller, keyword arguments, positional arguments! + if self.caller: + caller = kwargs.pop('caller', None) + if caller is None: + caller = self._environment.undefined('No caller defined', + name='caller') + arguments.append(caller) + if self.catch_kwargs: + arguments.append(kwargs) + elif kwargs: + raise TypeError('macro %r takes no keyword argument %r' % + (self.name, next(iter(kwargs)))) + if self.catch_varargs: + arguments.append(args[self._argument_count:]) + elif len(args) > self._argument_count: + raise TypeError('macro %r takes not more than %d argument(s)' % + (self.name, len(self.arguments))) + return self._func(*arguments) + + def __repr__(self): + return '<%s %s>' % ( + self.__class__.__name__, + self.name is None and 'anonymous' or repr(self.name) + ) + + +@implements_to_string +class Undefined(object): + """The default undefined type. This undefined type can be printed and + iterated over, but every other access will raise an :exc:`UndefinedError`: + + >>> foo = Undefined(name='foo') + >>> str(foo) + '' + >>> not foo + True + >>> foo + 42 + Traceback (most recent call last): + ... + UndefinedError: 'foo' is undefined + """ + __slots__ = ('_undefined_hint', '_undefined_obj', '_undefined_name', + '_undefined_exception') + + def __init__(self, hint=None, obj=missing, name=None, exc=UndefinedError): + self._undefined_hint = hint + self._undefined_obj = obj + self._undefined_name = name + self._undefined_exception = exc + + @internalcode + def _fail_with_undefined_error(self, *args, **kwargs): + """Regular callback function for undefined objects that raises an + `UndefinedError` on call. + """ + if self._undefined_hint is None: + if self._undefined_obj is missing: + hint = '%r is undefined' % self._undefined_name + elif not isinstance(self._undefined_name, string_types): + hint = '%s has no element %r' % ( + object_type_repr(self._undefined_obj), + self._undefined_name + ) + else: + hint = '%r has no attribute %r' % ( + object_type_repr(self._undefined_obj), + self._undefined_name + ) + else: + hint = self._undefined_hint + raise self._undefined_exception(hint) + + @internalcode + def __getattr__(self, name): + if name[:2] == '__': + raise AttributeError(name) + return self._fail_with_undefined_error() + + __add__ = __radd__ = __mul__ = __rmul__ = __div__ = __rdiv__ = \ + __truediv__ = __rtruediv__ = __floordiv__ = __rfloordiv__ = \ + __mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \ + __getitem__ = __lt__ = __le__ = __gt__ = __ge__ = __int__ = \ + __float__ = __complex__ = __pow__ = __rpow__ = \ + _fail_with_undefined_error + + def __eq__(self, other): + return type(self) is type(other) + + def __ne__(self, other): + return not self.__eq__(other) + + def __hash__(self): + return id(type(self)) + + def __str__(self): + return u'' + + def __len__(self): + return 0 + + def __iter__(self): + if 0: + yield None + + def __nonzero__(self): + return False + + def __repr__(self): + return 'Undefined' + + +@implements_to_string +class DebugUndefined(Undefined): + """An undefined that returns the debug info when printed. + + >>> foo = DebugUndefined(name='foo') + >>> str(foo) + '{{ foo }}' + >>> not foo + True + >>> foo + 42 + Traceback (most recent call last): + ... + UndefinedError: 'foo' is undefined + """ + __slots__ = () + + def __str__(self): + if self._undefined_hint is None: + if self._undefined_obj is missing: + return u'{{ %s }}' % self._undefined_name + return '{{ no such element: %s[%r] }}' % ( + object_type_repr(self._undefined_obj), + self._undefined_name + ) + return u'{{ undefined value printed: %s }}' % self._undefined_hint + + +@implements_to_string +class StrictUndefined(Undefined): + """An undefined that barks on print and iteration as well as boolean + tests and all kinds of comparisons. In other words: you can do nothing + with it except checking if it's defined using the `defined` test. + + >>> foo = StrictUndefined(name='foo') + >>> str(foo) + Traceback (most recent call last): + ... + UndefinedError: 'foo' is undefined + >>> not foo + Traceback (most recent call last): + ... + UndefinedError: 'foo' is undefined + >>> foo + 42 + Traceback (most recent call last): + ... + UndefinedError: 'foo' is undefined + """ + __slots__ = () + __iter__ = __str__ = __len__ = __nonzero__ = __eq__ = \ + __ne__ = __bool__ = __hash__ = \ + Undefined._fail_with_undefined_error + + +# remove remaining slots attributes, after the metaclass did the magic they +# are unneeded and irritating as they contain wrong data for the subclasses. +del Undefined.__slots__, DebugUndefined.__slots__, StrictUndefined.__slots__ diff --git a/mojo/public/third_party/jinja2/sandbox.py b/mojo/public/third_party/jinja2/sandbox.py new file mode 100644 index 0000000..da479c1 --- /dev/null +++ b/mojo/public/third_party/jinja2/sandbox.py @@ -0,0 +1,368 @@ +# -*- coding: utf-8 -*- +""" + jinja2.sandbox + ~~~~~~~~~~~~~~ + + Adds a sandbox layer to Jinja as it was the default behavior in the old + Jinja 1 releases. This sandbox is slightly different from Jinja 1 as the + default behavior is easier to use. + + The behavior can be changed by subclassing the environment. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD. +""" +import operator +from jinja2.environment import Environment +from jinja2.exceptions import SecurityError +from jinja2._compat import string_types, function_type, method_type, \ + traceback_type, code_type, frame_type, generator_type, PY2 + + +#: maximum number of items a range may produce +MAX_RANGE = 100000 + +#: attributes of function objects that are considered unsafe. +UNSAFE_FUNCTION_ATTRIBUTES = set(['func_closure', 'func_code', 'func_dict', + 'func_defaults', 'func_globals']) + +#: unsafe method attributes. function attributes are unsafe for methods too +UNSAFE_METHOD_ATTRIBUTES = set(['im_class', 'im_func', 'im_self']) + +#: unsafe generator attirbutes. +UNSAFE_GENERATOR_ATTRIBUTES = set(['gi_frame', 'gi_code']) + +# On versions > python 2 the special attributes on functions are gone, +# but they remain on methods and generators for whatever reason. +if not PY2: + UNSAFE_FUNCTION_ATTRIBUTES = set() + +import warnings + +# make sure we don't warn in python 2.6 about stuff we don't care about +warnings.filterwarnings('ignore', 'the sets module', DeprecationWarning, + module='jinja2.sandbox') + +from collections import deque + +_mutable_set_types = (set,) +_mutable_mapping_types = (dict,) +_mutable_sequence_types = (list,) + + +# on python 2.x we can register the user collection types +try: + from UserDict import UserDict, DictMixin + from UserList import UserList + _mutable_mapping_types += (UserDict, DictMixin) + _mutable_set_types += (UserList,) +except ImportError: + pass + +# if sets is still available, register the mutable set from there as well +try: + from sets import Set + _mutable_set_types += (Set,) +except ImportError: + pass + +#: register Python 2.6 abstract base classes +try: + from collections import MutableSet, MutableMapping, MutableSequence + _mutable_set_types += (MutableSet,) + _mutable_mapping_types += (MutableMapping,) + _mutable_sequence_types += (MutableSequence,) +except ImportError: + pass + +_mutable_spec = ( + (_mutable_set_types, frozenset([ + 'add', 'clear', 'difference_update', 'discard', 'pop', 'remove', + 'symmetric_difference_update', 'update' + ])), + (_mutable_mapping_types, frozenset([ + 'clear', 'pop', 'popitem', 'setdefault', 'update' + ])), + (_mutable_sequence_types, frozenset([ + 'append', 'reverse', 'insert', 'sort', 'extend', 'remove' + ])), + (deque, frozenset([ + 'append', 'appendleft', 'clear', 'extend', 'extendleft', 'pop', + 'popleft', 'remove', 'rotate' + ])) +) + + +def safe_range(*args): + """A range that can't generate ranges with a length of more than + MAX_RANGE items. + """ + rng = range(*args) + if len(rng) > MAX_RANGE: + raise OverflowError('range too big, maximum size for range is %d' % + MAX_RANGE) + return rng + + +def unsafe(f): + """Marks a function or method as unsafe. + + :: + + @unsafe + def delete(self): + pass + """ + f.unsafe_callable = True + return f + + +def is_internal_attribute(obj, attr): + """Test if the attribute given is an internal python attribute. For + example this function returns `True` for the `func_code` attribute of + python objects. This is useful if the environment method + :meth:`~SandboxedEnvironment.is_safe_attribute` is overridden. + + >>> from jinja2.sandbox import is_internal_attribute + >>> is_internal_attribute(lambda: None, "func_code") + True + >>> is_internal_attribute((lambda x:x).func_code, 'co_code') + True + >>> is_internal_attribute(str, "upper") + False + """ + if isinstance(obj, function_type): + if attr in UNSAFE_FUNCTION_ATTRIBUTES: + return True + elif isinstance(obj, method_type): + if attr in UNSAFE_FUNCTION_ATTRIBUTES or \ + attr in UNSAFE_METHOD_ATTRIBUTES: + return True + elif isinstance(obj, type): + if attr == 'mro': + return True + elif isinstance(obj, (code_type, traceback_type, frame_type)): + return True + elif isinstance(obj, generator_type): + if attr in UNSAFE_GENERATOR_ATTRIBUTES: + return True + return attr.startswith('__') + + +def modifies_known_mutable(obj, attr): + """This function checks if an attribute on a builtin mutable object + (list, dict, set or deque) would modify it if called. It also supports + the "user"-versions of the objects (`sets.Set`, `UserDict.*` etc.) and + with Python 2.6 onwards the abstract base classes `MutableSet`, + `MutableMapping`, and `MutableSequence`. + + >>> modifies_known_mutable({}, "clear") + True + >>> modifies_known_mutable({}, "keys") + False + >>> modifies_known_mutable([], "append") + True + >>> modifies_known_mutable([], "index") + False + + If called with an unsupported object (such as unicode) `False` is + returned. + + >>> modifies_known_mutable("foo", "upper") + False + """ + for typespec, unsafe in _mutable_spec: + if isinstance(obj, typespec): + return attr in unsafe + return False + + +class SandboxedEnvironment(Environment): + """The sandboxed environment. It works like the regular environment but + tells the compiler to generate sandboxed code. Additionally subclasses of + this environment may override the methods that tell the runtime what + attributes or functions are safe to access. + + If the template tries to access insecure code a :exc:`SecurityError` is + raised. However also other exceptions may occour during the rendering so + the caller has to ensure that all exceptions are catched. + """ + sandboxed = True + + #: default callback table for the binary operators. A copy of this is + #: available on each instance of a sandboxed environment as + #: :attr:`binop_table` + default_binop_table = { + '+': operator.add, + '-': operator.sub, + '*': operator.mul, + '/': operator.truediv, + '//': operator.floordiv, + '**': operator.pow, + '%': operator.mod + } + + #: default callback table for the unary operators. A copy of this is + #: available on each instance of a sandboxed environment as + #: :attr:`unop_table` + default_unop_table = { + '+': operator.pos, + '-': operator.neg + } + + #: a set of binary operators that should be intercepted. Each operator + #: that is added to this set (empty by default) is delegated to the + #: :meth:`call_binop` method that will perform the operator. The default + #: operator callback is specified by :attr:`binop_table`. + #: + #: The following binary operators are interceptable: + #: ``//``, ``%``, ``+``, ``*``, ``-``, ``/``, and ``**`` + #: + #: The default operation form the operator table corresponds to the + #: builtin function. Intercepted calls are always slower than the native + #: operator call, so make sure only to intercept the ones you are + #: interested in. + #: + #: .. versionadded:: 2.6 + intercepted_binops = frozenset() + + #: a set of unary operators that should be intercepted. Each operator + #: that is added to this set (empty by default) is delegated to the + #: :meth:`call_unop` method that will perform the operator. The default + #: operator callback is specified by :attr:`unop_table`. + #: + #: The following unary operators are interceptable: ``+``, ``-`` + #: + #: The default operation form the operator table corresponds to the + #: builtin function. Intercepted calls are always slower than the native + #: operator call, so make sure only to intercept the ones you are + #: interested in. + #: + #: .. versionadded:: 2.6 + intercepted_unops = frozenset() + + def intercept_unop(self, operator): + """Called during template compilation with the name of a unary + operator to check if it should be intercepted at runtime. If this + method returns `True`, :meth:`call_unop` is excuted for this unary + operator. The default implementation of :meth:`call_unop` will use + the :attr:`unop_table` dictionary to perform the operator with the + same logic as the builtin one. + + The following unary operators are interceptable: ``+`` and ``-`` + + Intercepted calls are always slower than the native operator call, + so make sure only to intercept the ones you are interested in. + + .. versionadded:: 2.6 + """ + return False + + + def __init__(self, *args, **kwargs): + Environment.__init__(self, *args, **kwargs) + self.globals['range'] = safe_range + self.binop_table = self.default_binop_table.copy() + self.unop_table = self.default_unop_table.copy() + + def is_safe_attribute(self, obj, attr, value): + """The sandboxed environment will call this method to check if the + attribute of an object is safe to access. Per default all attributes + starting with an underscore are considered private as well as the + special attributes of internal python objects as returned by the + :func:`is_internal_attribute` function. + """ + return not (attr.startswith('_') or is_internal_attribute(obj, attr)) + + def is_safe_callable(self, obj): + """Check if an object is safely callable. Per default a function is + considered safe unless the `unsafe_callable` attribute exists and is + True. Override this method to alter the behavior, but this won't + affect the `unsafe` decorator from this module. + """ + return not (getattr(obj, 'unsafe_callable', False) or + getattr(obj, 'alters_data', False)) + + def call_binop(self, context, operator, left, right): + """For intercepted binary operator calls (:meth:`intercepted_binops`) + this function is executed instead of the builtin operator. This can + be used to fine tune the behavior of certain operators. + + .. versionadded:: 2.6 + """ + return self.binop_table[operator](left, right) + + def call_unop(self, context, operator, arg): + """For intercepted unary operator calls (:meth:`intercepted_unops`) + this function is executed instead of the builtin operator. This can + be used to fine tune the behavior of certain operators. + + .. versionadded:: 2.6 + """ + return self.unop_table[operator](arg) + + def getitem(self, obj, argument): + """Subscribe an object from sandboxed code.""" + try: + return obj[argument] + except (TypeError, LookupError): + if isinstance(argument, string_types): + try: + attr = str(argument) + except Exception: + pass + else: + try: + value = getattr(obj, attr) + except AttributeError: + pass + else: + if self.is_safe_attribute(obj, argument, value): + return value + return self.unsafe_undefined(obj, argument) + return self.undefined(obj=obj, name=argument) + + def getattr(self, obj, attribute): + """Subscribe an object from sandboxed code and prefer the + attribute. The attribute passed *must* be a bytestring. + """ + try: + value = getattr(obj, attribute) + except AttributeError: + try: + return obj[attribute] + except (TypeError, LookupError): + pass + else: + if self.is_safe_attribute(obj, attribute, value): + return value + return self.unsafe_undefined(obj, attribute) + return self.undefined(obj=obj, name=attribute) + + def unsafe_undefined(self, obj, attribute): + """Return an undefined object for unsafe attributes.""" + return self.undefined('access to attribute %r of %r ' + 'object is unsafe.' % ( + attribute, + obj.__class__.__name__ + ), name=attribute, obj=obj, exc=SecurityError) + + def call(__self, __context, __obj, *args, **kwargs): + """Call an object from sandboxed code.""" + # the double prefixes are to avoid double keyword argument + # errors when proxying the call. + if not __self.is_safe_callable(__obj): + raise SecurityError('%r is not safely callable' % (__obj,)) + return __context.call(__obj, *args, **kwargs) + + +class ImmutableSandboxedEnvironment(SandboxedEnvironment): + """Works exactly like the regular `SandboxedEnvironment` but does not + permit modifications on the builtin mutable objects `list`, `set`, and + `dict` by using the :func:`modifies_known_mutable` function. + """ + + def is_safe_attribute(self, obj, attr, value): + if not SandboxedEnvironment.is_safe_attribute(self, obj, attr, value): + return False + return not modifies_known_mutable(obj, attr) diff --git a/mojo/public/third_party/jinja2/tests.py b/mojo/public/third_party/jinja2/tests.py new file mode 100644 index 0000000..48a3e06 --- /dev/null +++ b/mojo/public/third_party/jinja2/tests.py @@ -0,0 +1,149 @@ +# -*- coding: utf-8 -*- +""" + jinja2.tests + ~~~~~~~~~~~~ + + Jinja test functions. Used with the "is" operator. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import re +from jinja2.runtime import Undefined +from jinja2._compat import text_type, string_types, mapping_types + + +number_re = re.compile(r'^-?\d+(\.\d+)?$') +regex_type = type(number_re) + + +test_callable = callable + + +def test_odd(value): + """Return true if the variable is odd.""" + return value % 2 == 1 + + +def test_even(value): + """Return true if the variable is even.""" + return value % 2 == 0 + + +def test_divisibleby(value, num): + """Check if a variable is divisible by a number.""" + return value % num == 0 + + +def test_defined(value): + """Return true if the variable is defined: + + .. sourcecode:: jinja + + {% if variable is defined %} + value of variable: {{ variable }} + {% else %} + variable is not defined + {% endif %} + + See the :func:`default` filter for a simple way to set undefined + variables. + """ + return not isinstance(value, Undefined) + + +def test_undefined(value): + """Like :func:`defined` but the other way round.""" + return isinstance(value, Undefined) + + +def test_none(value): + """Return true if the variable is none.""" + return value is None + + +def test_lower(value): + """Return true if the variable is lowercased.""" + return text_type(value).islower() + + +def test_upper(value): + """Return true if the variable is uppercased.""" + return text_type(value).isupper() + + +def test_string(value): + """Return true if the object is a string.""" + return isinstance(value, string_types) + + +def test_mapping(value): + """Return true if the object is a mapping (dict etc.). + + .. versionadded:: 2.6 + """ + return isinstance(value, mapping_types) + + +def test_number(value): + """Return true if the variable is a number.""" + return isinstance(value, (int, float, complex)) + + +def test_sequence(value): + """Return true if the variable is a sequence. Sequences are variables + that are iterable. + """ + try: + len(value) + value.__getitem__ + except: + return False + return True + + +def test_sameas(value, other): + """Check if an object points to the same memory address than another + object: + + .. sourcecode:: jinja + + {% if foo.attribute is sameas false %} + the foo attribute really is the `False` singleton + {% endif %} + """ + return value is other + + +def test_iterable(value): + """Check if it's possible to iterate over an object.""" + try: + iter(value) + except TypeError: + return False + return True + + +def test_escaped(value): + """Check if the value is escaped.""" + return hasattr(value, '__html__') + + +TESTS = { + 'odd': test_odd, + 'even': test_even, + 'divisibleby': test_divisibleby, + 'defined': test_defined, + 'undefined': test_undefined, + 'none': test_none, + 'lower': test_lower, + 'upper': test_upper, + 'string': test_string, + 'mapping': test_mapping, + 'number': test_number, + 'sequence': test_sequence, + 'iterable': test_iterable, + 'callable': test_callable, + 'sameas': test_sameas, + 'escaped': test_escaped +} diff --git a/mojo/public/third_party/jinja2/utils.py b/mojo/public/third_party/jinja2/utils.py new file mode 100644 index 0000000..ddc47da --- /dev/null +++ b/mojo/public/third_party/jinja2/utils.py @@ -0,0 +1,520 @@ +# -*- coding: utf-8 -*- +""" + jinja2.utils + ~~~~~~~~~~~~ + + Utility functions. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import re +import errno +from collections import deque +from jinja2._compat import text_type, string_types, implements_iterator, \ + allocate_lock, url_quote + + +_word_split_re = re.compile(r'(\s+)') +_punctuation_re = re.compile( + '^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$' % ( + '|'.join(map(re.escape, ('(', '<', '<'))), + '|'.join(map(re.escape, ('.', ',', ')', '>', '\n', '>'))) + ) +) +_simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$') +_striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)') +_entity_re = re.compile(r'&([^;]+);') +_letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' +_digits = '0123456789' + +# special singleton representing missing values for the runtime +missing = type('MissingType', (), {'__repr__': lambda x: 'missing'})() + +# internal code +internal_code = set() + +concat = u''.join + + +def contextfunction(f): + """This decorator can be used to mark a function or method context callable. + A context callable is passed the active :class:`Context` as first argument when + called from the template. This is useful if a function wants to get access + to the context or functions provided on the context object. For example + a function that returns a sorted list of template variables the current + template exports could look like this:: + + @contextfunction + def get_exported_names(context): + return sorted(context.exported_vars) + """ + f.contextfunction = True + return f + + +def evalcontextfunction(f): + """This decorator can be used to mark a function or method as an eval + context callable. This is similar to the :func:`contextfunction` + but instead of passing the context, an evaluation context object is + passed. For more information about the eval context, see + :ref:`eval-context`. + + .. versionadded:: 2.4 + """ + f.evalcontextfunction = True + return f + + +def environmentfunction(f): + """This decorator can be used to mark a function or method as environment + callable. This decorator works exactly like the :func:`contextfunction` + decorator just that the first argument is the active :class:`Environment` + and not context. + """ + f.environmentfunction = True + return f + + +def internalcode(f): + """Marks the function as internally used""" + internal_code.add(f.__code__) + return f + + +def is_undefined(obj): + """Check if the object passed is undefined. This does nothing more than + performing an instance check against :class:`Undefined` but looks nicer. + This can be used for custom filters or tests that want to react to + undefined variables. For example a custom default filter can look like + this:: + + def default(var, default=''): + if is_undefined(var): + return default + return var + """ + from jinja2.runtime import Undefined + return isinstance(obj, Undefined) + + +def consume(iterable): + """Consumes an iterable without doing anything with it.""" + for event in iterable: + pass + + +def clear_caches(): + """Jinja2 keeps internal caches for environments and lexers. These are + used so that Jinja2 doesn't have to recreate environments and lexers all + the time. Normally you don't have to care about that but if you are + messuring memory consumption you may want to clean the caches. + """ + from jinja2.environment import _spontaneous_environments + from jinja2.lexer import _lexer_cache + _spontaneous_environments.clear() + _lexer_cache.clear() + + +def import_string(import_name, silent=False): + """Imports an object based on a string. This is useful if you want to + use import paths as endpoints or something similar. An import path can + be specified either in dotted notation (``xml.sax.saxutils.escape``) + or with a colon as object delimiter (``xml.sax.saxutils:escape``). + + If the `silent` is True the return value will be `None` if the import + fails. + + :return: imported object + """ + try: + if ':' in import_name: + module, obj = import_name.split(':', 1) + elif '.' in import_name: + items = import_name.split('.') + module = '.'.join(items[:-1]) + obj = items[-1] + else: + return __import__(import_name) + return getattr(__import__(module, None, None, [obj]), obj) + except (ImportError, AttributeError): + if not silent: + raise + + +def open_if_exists(filename, mode='rb'): + """Returns a file descriptor for the filename if that file exists, + otherwise `None`. + """ + try: + return open(filename, mode) + except IOError as e: + if e.errno not in (errno.ENOENT, errno.EISDIR): + raise + + +def object_type_repr(obj): + """Returns the name of the object's type. For some recognized + singletons the name of the object is returned instead. (For + example for `None` and `Ellipsis`). + """ + if obj is None: + return 'None' + elif obj is Ellipsis: + return 'Ellipsis' + # __builtin__ in 2.x, builtins in 3.x + if obj.__class__.__module__ in ('__builtin__', 'builtins'): + name = obj.__class__.__name__ + else: + name = obj.__class__.__module__ + '.' + obj.__class__.__name__ + return '%s object' % name + + +def pformat(obj, verbose=False): + """Prettyprint an object. Either use the `pretty` library or the + builtin `pprint`. + """ + try: + from pretty import pretty + return pretty(obj, verbose=verbose) + except ImportError: + from pprint import pformat + return pformat(obj) + + +def urlize(text, trim_url_limit=None, nofollow=False): + """Converts any URLs in text into clickable links. Works on http://, + https:// and www. links. Links can have trailing punctuation (periods, + commas, close-parens) and leading punctuation (opening parens) and + it'll still do the right thing. + + If trim_url_limit is not None, the URLs in link text will be limited + to trim_url_limit characters. + + If nofollow is True, the URLs in link text will get a rel="nofollow" + attribute. + """ + trim_url = lambda x, limit=trim_url_limit: limit is not None \ + and (x[:limit] + (len(x) >=limit and '...' + or '')) or x + words = _word_split_re.split(text_type(escape(text))) + nofollow_attr = nofollow and ' rel="nofollow"' or '' + for i, word in enumerate(words): + match = _punctuation_re.match(word) + if match: + lead, middle, trail = match.groups() + if middle.startswith('www.') or ( + '@' not in middle and + not middle.startswith('http://') and + not middle.startswith('https://') and + len(middle) > 0 and + middle[0] in _letters + _digits and ( + middle.endswith('.org') or + middle.endswith('.net') or + middle.endswith('.com') + )): + middle = '<a href="http://%s"%s>%s</a>' % (middle, + nofollow_attr, trim_url(middle)) + if middle.startswith('http://') or \ + middle.startswith('https://'): + middle = '<a href="%s"%s>%s</a>' % (middle, + nofollow_attr, trim_url(middle)) + if '@' in middle and not middle.startswith('www.') and \ + not ':' in middle and _simple_email_re.match(middle): + middle = '<a href="mailto:%s">%s</a>' % (middle, middle) + if lead + middle + trail != word: + words[i] = lead + middle + trail + return u''.join(words) + + +def generate_lorem_ipsum(n=5, html=True, min=20, max=100): + """Generate some lorem impsum for the template.""" + from jinja2.constants import LOREM_IPSUM_WORDS + from random import choice, randrange + words = LOREM_IPSUM_WORDS.split() + result = [] + + for _ in range(n): + next_capitalized = True + last_comma = last_fullstop = 0 + word = None + last = None + p = [] + + # each paragraph contains out of 20 to 100 words. + for idx, _ in enumerate(range(randrange(min, max))): + while True: + word = choice(words) + if word != last: + last = word + break + if next_capitalized: + word = word.capitalize() + next_capitalized = False + # add commas + if idx - randrange(3, 8) > last_comma: + last_comma = idx + last_fullstop += 2 + word += ',' + # add end of sentences + if idx - randrange(10, 20) > last_fullstop: + last_comma = last_fullstop = idx + word += '.' + next_capitalized = True + p.append(word) + + # ensure that the paragraph ends with a dot. + p = u' '.join(p) + if p.endswith(','): + p = p[:-1] + '.' + elif not p.endswith('.'): + p += '.' + result.append(p) + + if not html: + return u'\n\n'.join(result) + return Markup(u'\n'.join(u'<p>%s</p>' % escape(x) for x in result)) + + +def unicode_urlencode(obj, charset='utf-8'): + """URL escapes a single bytestring or unicode string with the + given charset if applicable to URL safe quoting under all rules + that need to be considered under all supported Python versions. + + If non strings are provided they are converted to their unicode + representation first. + """ + if not isinstance(obj, string_types): + obj = text_type(obj) + if isinstance(obj, text_type): + obj = obj.encode(charset) + return text_type(url_quote(obj)) + + +class LRUCache(object): + """A simple LRU Cache implementation.""" + + # this is fast for small capacities (something below 1000) but doesn't + # scale. But as long as it's only used as storage for templates this + # won't do any harm. + + def __init__(self, capacity): + self.capacity = capacity + self._mapping = {} + self._queue = deque() + self._postinit() + + def _postinit(self): + # alias all queue methods for faster lookup + self._popleft = self._queue.popleft + self._pop = self._queue.pop + self._remove = self._queue.remove + self._wlock = allocate_lock() + self._append = self._queue.append + + def __getstate__(self): + return { + 'capacity': self.capacity, + '_mapping': self._mapping, + '_queue': self._queue + } + + def __setstate__(self, d): + self.__dict__.update(d) + self._postinit() + + def __getnewargs__(self): + return (self.capacity,) + + def copy(self): + """Return a shallow copy of the instance.""" + rv = self.__class__(self.capacity) + rv._mapping.update(self._mapping) + rv._queue = deque(self._queue) + return rv + + def get(self, key, default=None): + """Return an item from the cache dict or `default`""" + try: + return self[key] + except KeyError: + return default + + def setdefault(self, key, default=None): + """Set `default` if the key is not in the cache otherwise + leave unchanged. Return the value of this key. + """ + self._wlock.acquire() + try: + try: + return self[key] + except KeyError: + self[key] = default + return default + finally: + self._wlock.release() + + def clear(self): + """Clear the cache.""" + self._wlock.acquire() + try: + self._mapping.clear() + self._queue.clear() + finally: + self._wlock.release() + + def __contains__(self, key): + """Check if a key exists in this cache.""" + return key in self._mapping + + def __len__(self): + """Return the current size of the cache.""" + return len(self._mapping) + + def __repr__(self): + return '<%s %r>' % ( + self.__class__.__name__, + self._mapping + ) + + def __getitem__(self, key): + """Get an item from the cache. Moves the item up so that it has the + highest priority then. + + Raise a `KeyError` if it does not exist. + """ + self._wlock.acquire() + try: + rv = self._mapping[key] + if self._queue[-1] != key: + try: + self._remove(key) + except ValueError: + # if something removed the key from the container + # when we read, ignore the ValueError that we would + # get otherwise. + pass + self._append(key) + return rv + finally: + self._wlock.release() + + def __setitem__(self, key, value): + """Sets the value for an item. Moves the item up so that it + has the highest priority then. + """ + self._wlock.acquire() + try: + if key in self._mapping: + self._remove(key) + elif len(self._mapping) == self.capacity: + del self._mapping[self._popleft()] + self._append(key) + self._mapping[key] = value + finally: + self._wlock.release() + + def __delitem__(self, key): + """Remove an item from the cache dict. + Raise a `KeyError` if it does not exist. + """ + self._wlock.acquire() + try: + del self._mapping[key] + try: + self._remove(key) + except ValueError: + # __getitem__ is not locked, it might happen + pass + finally: + self._wlock.release() + + def items(self): + """Return a list of items.""" + result = [(key, self._mapping[key]) for key in list(self._queue)] + result.reverse() + return result + + def iteritems(self): + """Iterate over all items.""" + return iter(self.items()) + + def values(self): + """Return a list of all values.""" + return [x[1] for x in self.items()] + + def itervalue(self): + """Iterate over all values.""" + return iter(self.values()) + + def keys(self): + """Return a list of all keys ordered by most recent usage.""" + return list(self) + + def iterkeys(self): + """Iterate over all keys in the cache dict, ordered by + the most recent usage. + """ + return reversed(tuple(self._queue)) + + __iter__ = iterkeys + + def __reversed__(self): + """Iterate over the values in the cache dict, oldest items + coming first. + """ + return iter(tuple(self._queue)) + + __copy__ = copy + + +# register the LRU cache as mutable mapping if possible +try: + from collections import MutableMapping + MutableMapping.register(LRUCache) +except ImportError: + pass + + +@implements_iterator +class Cycler(object): + """A cycle helper for templates.""" + + def __init__(self, *items): + if not items: + raise RuntimeError('at least one item has to be provided') + self.items = items + self.reset() + + def reset(self): + """Resets the cycle.""" + self.pos = 0 + + @property + def current(self): + """Returns the current item.""" + return self.items[self.pos] + + def __next__(self): + """Goes one item ahead and returns it.""" + rv = self.current + self.pos = (self.pos + 1) % len(self.items) + return rv + + +class Joiner(object): + """A joining helper for templates.""" + + def __init__(self, sep=u', '): + self.sep = sep + self.used = False + + def __call__(self): + if not self.used: + self.used = True + return u'' + return self.sep + + +# Imported here because that's where it was in the past +from markupsafe import Markup, escape, soft_unicode diff --git a/mojo/public/third_party/jinja2/visitor.py b/mojo/public/third_party/jinja2/visitor.py new file mode 100644 index 0000000..413e7c3 --- /dev/null +++ b/mojo/public/third_party/jinja2/visitor.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- +""" + jinja2.visitor + ~~~~~~~~~~~~~~ + + This module implements a visitor for the nodes. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD. +""" +from jinja2.nodes import Node + + +class NodeVisitor(object): + """Walks the abstract syntax tree and call visitor functions for every + node found. The visitor functions may return values which will be + forwarded by the `visit` method. + + Per default the visitor functions for the nodes are ``'visit_'`` + + class name of the node. So a `TryFinally` node visit function would + be `visit_TryFinally`. This behavior can be changed by overriding + the `get_visitor` function. If no visitor function exists for a node + (return value `None`) the `generic_visit` visitor is used instead. + """ + + def get_visitor(self, node): + """Return the visitor function for this node or `None` if no visitor + exists for this node. In that case the generic visit function is + used instead. + """ + method = 'visit_' + node.__class__.__name__ + return getattr(self, method, None) + + def visit(self, node, *args, **kwargs): + """Visit a node.""" + f = self.get_visitor(node) + if f is not None: + return f(node, *args, **kwargs) + return self.generic_visit(node, *args, **kwargs) + + def generic_visit(self, node, *args, **kwargs): + """Called if no explicit visitor function exists for a node.""" + for node in node.iter_child_nodes(): + self.visit(node, *args, **kwargs) + + +class NodeTransformer(NodeVisitor): + """Walks the abstract syntax tree and allows modifications of nodes. + + The `NodeTransformer` will walk the AST and use the return value of the + visitor functions to replace or remove the old node. If the return + value of the visitor function is `None` the node will be removed + from the previous location otherwise it's replaced with the return + value. The return value may be the original node in which case no + replacement takes place. + """ + + def generic_visit(self, node, *args, **kwargs): + for field, old_value in node.iter_fields(): + if isinstance(old_value, list): + new_values = [] + for value in old_value: + if isinstance(value, Node): + value = self.visit(value, *args, **kwargs) + if value is None: + continue + elif not isinstance(value, Node): + new_values.extend(value) + continue + new_values.append(value) + old_value[:] = new_values + elif isinstance(old_value, Node): + new_node = self.visit(old_value, *args, **kwargs) + if new_node is None: + delattr(node, field) + else: + setattr(node, field, new_node) + return node + + def visit_list(self, node, *args, **kwargs): + """As transformers may return lists in some places this method + can be used to enforce a list as return value. + """ + rv = self.visit(node, *args, **kwargs) + if not isinstance(rv, list): + rv = [rv] + return rv diff --git a/mojo/public/third_party/markupsafe/AUTHORS b/mojo/public/third_party/markupsafe/AUTHORS new file mode 100644 index 0000000..f7e2942 --- /dev/null +++ b/mojo/public/third_party/markupsafe/AUTHORS @@ -0,0 +1,13 @@ +MarkupSafe is written and maintained by Armin Ronacher and +various contributors: + +Development Lead +```````````````` + +- Armin Ronacher <armin.ronacher@active-4.com> + +Patches and Suggestions +``````````````````````` + +- Georg Brandl +- Mickaël Guérin diff --git a/mojo/public/third_party/markupsafe/LICENSE b/mojo/public/third_party/markupsafe/LICENSE new file mode 100644 index 0000000..5d26938 --- /dev/null +++ b/mojo/public/third_party/markupsafe/LICENSE @@ -0,0 +1,33 @@ +Copyright (c) 2010 by Armin Ronacher and contributors. See AUTHORS +for more details. + +Some rights reserved. + +Redistribution and use in source and binary forms of the software as well +as documentation, with or without modification, are permitted provided +that the following conditions are met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + +* The names of the contributors may not be used to endorse or + promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT +NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. diff --git a/mojo/public/third_party/markupsafe/MarkupSafe-0.18.tar.gz.md5 b/mojo/public/third_party/markupsafe/MarkupSafe-0.18.tar.gz.md5 new file mode 100644 index 0000000..1348d1e --- /dev/null +++ b/mojo/public/third_party/markupsafe/MarkupSafe-0.18.tar.gz.md5 @@ -0,0 +1 @@ +f8d252fd05371e51dec2fe9a36890687 MarkupSafe-0.18.tar.gz diff --git a/mojo/public/third_party/markupsafe/MarkupSafe-0.18.tar.gz.sha512 b/mojo/public/third_party/markupsafe/MarkupSafe-0.18.tar.gz.sha512 new file mode 100644 index 0000000..ab75220 --- /dev/null +++ b/mojo/public/third_party/markupsafe/MarkupSafe-0.18.tar.gz.sha512 @@ -0,0 +1 @@ +0438ddf0fdab465c40d9afba8c14ad346be0868df654c11130d05e329992d456a9bc278551970cbd09244a29c77213885d0c363c951b0cfd4d9aa95b248ecff5 MarkupSafe-0.18.tar.gz diff --git a/mojo/public/third_party/markupsafe/OWNERS b/mojo/public/third_party/markupsafe/OWNERS new file mode 100644 index 0000000..8edbdf8 --- /dev/null +++ b/mojo/public/third_party/markupsafe/OWNERS @@ -0,0 +1,3 @@ +timloh@chromium.org +haraken@chromium.org +nbarth@chromium.org diff --git a/mojo/public/third_party/markupsafe/README.chromium b/mojo/public/third_party/markupsafe/README.chromium new file mode 100644 index 0000000..0fcab52 --- /dev/null +++ b/mojo/public/third_party/markupsafe/README.chromium @@ -0,0 +1,24 @@ +Name: MarkupSafe Python Safe String Class +Short Name: markupsafe +URL: https://github.com/mitsuhiko/markupsafe +Version: 0.18 +License: BSD 3-clause License +License File: NOT_SHIPPED +Security Critical: no + +Description: +Safe string class, used by Jinja2 template engine. + +Source: +https://pypi.python.org/packages/source/M/MarkupSafe/MarkupSafe-0.18.tar.gz +MD5: f8d252fd05371e51dec2fe9a36890687 +SHA-512: 0438ddf0fdab465c40d9afba8c14ad346be0868df654c11130d05e329992d456 + a9bc278551970cbd09244a29c77213885d0c363c951b0cfd4d9aa95b248ecff5 + +Local Modifications: +This only includes the markup directory from the tarball and the LICENSE and +AUTHORS files, removing the unneeded unit tests (tests.py). +Also includes install script (get_markupsafe.sh) and files of hashes (MD5 is +also posted on website, SHA-512 computed locally); script checks hash then +unpacks archive and installs desired files. +Retrieve or update by executing markupsafe/get_markupsafe.sh from third_party. diff --git a/mojo/public/third_party/markupsafe/__init__.py b/mojo/public/third_party/markupsafe/__init__.py new file mode 100644 index 0000000..25f00d3 --- /dev/null +++ b/mojo/public/third_party/markupsafe/__init__.py @@ -0,0 +1,234 @@ +# -*- coding: utf-8 -*- +""" + markupsafe + ~~~~~~~~~~ + + Implements a Markup string. + + :copyright: (c) 2010 by Armin Ronacher. + :license: BSD, see LICENSE for more details. +""" +import re +from markupsafe._compat import text_type, string_types, int_types, \ + unichr, PY2 + + +__all__ = ['Markup', 'soft_unicode', 'escape', 'escape_silent'] + + +_striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)') +_entity_re = re.compile(r'&([^;]+);') + + +class Markup(text_type): + r"""Marks a string as being safe for inclusion in HTML/XML output without + needing to be escaped. This implements the `__html__` interface a couple + of frameworks and web applications use. :class:`Markup` is a direct + subclass of `unicode` and provides all the methods of `unicode` just that + it escapes arguments passed and always returns `Markup`. + + The `escape` function returns markup objects so that double escaping can't + happen. + + The constructor of the :class:`Markup` class can be used for three + different things: When passed an unicode object it's assumed to be safe, + when passed an object with an HTML representation (has an `__html__` + method) that representation is used, otherwise the object passed is + converted into a unicode string and then assumed to be safe: + + >>> Markup("Hello <em>World</em>!") + Markup(u'Hello <em>World</em>!') + >>> class Foo(object): + ... def __html__(self): + ... return '<a href="#">foo</a>' + ... + >>> Markup(Foo()) + Markup(u'<a href="#">foo</a>') + + If you want object passed being always treated as unsafe you can use the + :meth:`escape` classmethod to create a :class:`Markup` object: + + >>> Markup.escape("Hello <em>World</em>!") + Markup(u'Hello <em>World</em>!') + + Operations on a markup string are markup aware which means that all + arguments are passed through the :func:`escape` function: + + >>> em = Markup("<em>%s</em>") + >>> em % "foo & bar" + Markup(u'<em>foo & bar</em>') + >>> strong = Markup("<strong>%(text)s</strong>") + >>> strong % {'text': '<blink>hacker here</blink>'} + Markup(u'<strong><blink>hacker here</blink></strong>') + >>> Markup("<em>Hello</em> ") + "<foo>" + Markup(u'<em>Hello</em> <foo>') + """ + __slots__ = () + + def __new__(cls, base=u'', encoding=None, errors='strict'): + if hasattr(base, '__html__'): + base = base.__html__() + if encoding is None: + return text_type.__new__(cls, base) + return text_type.__new__(cls, base, encoding, errors) + + def __html__(self): + return self + + def __add__(self, other): + if isinstance(other, string_types) or hasattr(other, '__html__'): + return self.__class__(super(Markup, self).__add__(self.escape(other))) + return NotImplemented + + def __radd__(self, other): + if hasattr(other, '__html__') or isinstance(other, string_types): + return self.escape(other).__add__(self) + return NotImplemented + + def __mul__(self, num): + if isinstance(num, int_types): + return self.__class__(text_type.__mul__(self, num)) + return NotImplemented + __rmul__ = __mul__ + + def __mod__(self, arg): + if isinstance(arg, tuple): + arg = tuple(_MarkupEscapeHelper(x, self.escape) for x in arg) + else: + arg = _MarkupEscapeHelper(arg, self.escape) + return self.__class__(text_type.__mod__(self, arg)) + + def __repr__(self): + return '%s(%s)' % ( + self.__class__.__name__, + text_type.__repr__(self) + ) + + def join(self, seq): + return self.__class__(text_type.join(self, map(self.escape, seq))) + join.__doc__ = text_type.join.__doc__ + + def split(self, *args, **kwargs): + return list(map(self.__class__, text_type.split(self, *args, **kwargs))) + split.__doc__ = text_type.split.__doc__ + + def rsplit(self, *args, **kwargs): + return list(map(self.__class__, text_type.rsplit(self, *args, **kwargs))) + rsplit.__doc__ = text_type.rsplit.__doc__ + + def splitlines(self, *args, **kwargs): + return list(map(self.__class__, text_type.splitlines(self, *args, **kwargs))) + splitlines.__doc__ = text_type.splitlines.__doc__ + + def unescape(self): + r"""Unescape markup again into an text_type string. This also resolves + known HTML4 and XHTML entities: + + >>> Markup("Main » <em>About</em>").unescape() + u'Main \xbb <em>About</em>' + """ + from markupsafe._constants import HTML_ENTITIES + def handle_match(m): + name = m.group(1) + if name in HTML_ENTITIES: + return unichr(HTML_ENTITIES[name]) + try: + if name[:2] in ('#x', '#X'): + return unichr(int(name[2:], 16)) + elif name.startswith('#'): + return unichr(int(name[1:])) + except ValueError: + pass + return u'' + return _entity_re.sub(handle_match, text_type(self)) + + def striptags(self): + r"""Unescape markup into an text_type string and strip all tags. This + also resolves known HTML4 and XHTML entities. Whitespace is + normalized to one: + + >>> Markup("Main » <em>About</em>").striptags() + u'Main \xbb About' + """ + stripped = u' '.join(_striptags_re.sub('', self).split()) + return Markup(stripped).unescape() + + @classmethod + def escape(cls, s): + """Escape the string. Works like :func:`escape` with the difference + that for subclasses of :class:`Markup` this function would return the + correct subclass. + """ + rv = escape(s) + if rv.__class__ is not cls: + return cls(rv) + return rv + + def make_wrapper(name): + orig = getattr(text_type, name) + def func(self, *args, **kwargs): + args = _escape_argspec(list(args), enumerate(args), self.escape) + #_escape_argspec(kwargs, kwargs.iteritems(), None) + return self.__class__(orig(self, *args, **kwargs)) + func.__name__ = orig.__name__ + func.__doc__ = orig.__doc__ + return func + + for method in '__getitem__', 'capitalize', \ + 'title', 'lower', 'upper', 'replace', 'ljust', \ + 'rjust', 'lstrip', 'rstrip', 'center', 'strip', \ + 'translate', 'expandtabs', 'swapcase', 'zfill': + locals()[method] = make_wrapper(method) + + # new in python 2.5 + if hasattr(text_type, 'partition'): + def partition(self, sep): + return tuple(map(self.__class__, + text_type.partition(self, self.escape(sep)))) + def rpartition(self, sep): + return tuple(map(self.__class__, + text_type.rpartition(self, self.escape(sep)))) + + # new in python 2.6 + if hasattr(text_type, 'format'): + format = make_wrapper('format') + + # not in python 3 + if hasattr(text_type, '__getslice__'): + __getslice__ = make_wrapper('__getslice__') + + del method, make_wrapper + + +def _escape_argspec(obj, iterable, escape): + """Helper for various string-wrapped functions.""" + for key, value in iterable: + if hasattr(value, '__html__') or isinstance(value, string_types): + obj[key] = escape(value) + return obj + + +class _MarkupEscapeHelper(object): + """Helper for Markup.__mod__""" + + def __init__(self, obj, escape): + self.obj = obj + self.escape = escape + + __getitem__ = lambda s, x: _MarkupEscapeHelper(s.obj[x], s.escape) + __unicode__ = __str__ = lambda s: text_type(s.escape(s.obj)) + __repr__ = lambda s: str(s.escape(repr(s.obj))) + __int__ = lambda s: int(s.obj) + __float__ = lambda s: float(s.obj) + + +# we have to import it down here as the speedups and native +# modules imports the markup type which is define above. +try: + from markupsafe._speedups import escape, escape_silent, soft_unicode +except ImportError: + from markupsafe._native import escape, escape_silent, soft_unicode + +if not PY2: + soft_str = soft_unicode + __all__.append('soft_str') diff --git a/mojo/public/third_party/markupsafe/_compat.py b/mojo/public/third_party/markupsafe/_compat.py new file mode 100644 index 0000000..29e4a3d --- /dev/null +++ b/mojo/public/third_party/markupsafe/_compat.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +""" + markupsafe._compat + ~~~~~~~~~~~~~~~~~~ + + Compatibility module for different Python versions. + + :copyright: (c) 2013 by Armin Ronacher. + :license: BSD, see LICENSE for more details. +""" +import sys + +PY2 = sys.version_info[0] == 2 + +if not PY2: + text_type = str + string_types = (str,) + unichr = chr + int_types = (int,) +else: + text_type = unicode + string_types = (str, unicode) + unichr = unichr + int_types = (int, long) diff --git a/mojo/public/third_party/markupsafe/_constants.py b/mojo/public/third_party/markupsafe/_constants.py new file mode 100644 index 0000000..919bf03 --- /dev/null +++ b/mojo/public/third_party/markupsafe/_constants.py @@ -0,0 +1,267 @@ +# -*- coding: utf-8 -*- +""" + markupsafe._constants + ~~~~~~~~~~~~~~~~~~~~~ + + Highlevel implementation of the Markup string. + + :copyright: (c) 2010 by Armin Ronacher. + :license: BSD, see LICENSE for more details. +""" + + +HTML_ENTITIES = { + 'AElig': 198, + 'Aacute': 193, + 'Acirc': 194, + 'Agrave': 192, + 'Alpha': 913, + 'Aring': 197, + 'Atilde': 195, + 'Auml': 196, + 'Beta': 914, + 'Ccedil': 199, + 'Chi': 935, + 'Dagger': 8225, + 'Delta': 916, + 'ETH': 208, + 'Eacute': 201, + 'Ecirc': 202, + 'Egrave': 200, + 'Epsilon': 917, + 'Eta': 919, + 'Euml': 203, + 'Gamma': 915, + 'Iacute': 205, + 'Icirc': 206, + 'Igrave': 204, + 'Iota': 921, + 'Iuml': 207, + 'Kappa': 922, + 'Lambda': 923, + 'Mu': 924, + 'Ntilde': 209, + 'Nu': 925, + 'OElig': 338, + 'Oacute': 211, + 'Ocirc': 212, + 'Ograve': 210, + 'Omega': 937, + 'Omicron': 927, + 'Oslash': 216, + 'Otilde': 213, + 'Ouml': 214, + 'Phi': 934, + 'Pi': 928, + 'Prime': 8243, + 'Psi': 936, + 'Rho': 929, + 'Scaron': 352, + 'Sigma': 931, + 'THORN': 222, + 'Tau': 932, + 'Theta': 920, + 'Uacute': 218, + 'Ucirc': 219, + 'Ugrave': 217, + 'Upsilon': 933, + 'Uuml': 220, + 'Xi': 926, + 'Yacute': 221, + 'Yuml': 376, + 'Zeta': 918, + 'aacute': 225, + 'acirc': 226, + 'acute': 180, + 'aelig': 230, + 'agrave': 224, + 'alefsym': 8501, + 'alpha': 945, + 'amp': 38, + 'and': 8743, + 'ang': 8736, + 'apos': 39, + 'aring': 229, + 'asymp': 8776, + 'atilde': 227, + 'auml': 228, + 'bdquo': 8222, + 'beta': 946, + 'brvbar': 166, + 'bull': 8226, + 'cap': 8745, + 'ccedil': 231, + 'cedil': 184, + 'cent': 162, + 'chi': 967, + 'circ': 710, + 'clubs': 9827, + 'cong': 8773, + 'copy': 169, + 'crarr': 8629, + 'cup': 8746, + 'curren': 164, + 'dArr': 8659, + 'dagger': 8224, + 'darr': 8595, + 'deg': 176, + 'delta': 948, + 'diams': 9830, + 'divide': 247, + 'eacute': 233, + 'ecirc': 234, + 'egrave': 232, + 'empty': 8709, + 'emsp': 8195, + 'ensp': 8194, + 'epsilon': 949, + 'equiv': 8801, + 'eta': 951, + 'eth': 240, + 'euml': 235, + 'euro': 8364, + 'exist': 8707, + 'fnof': 402, + 'forall': 8704, + 'frac12': 189, + 'frac14': 188, + 'frac34': 190, + 'frasl': 8260, + 'gamma': 947, + 'ge': 8805, + 'gt': 62, + 'hArr': 8660, + 'harr': 8596, + 'hearts': 9829, + 'hellip': 8230, + 'iacute': 237, + 'icirc': 238, + 'iexcl': 161, + 'igrave': 236, + 'image': 8465, + 'infin': 8734, + 'int': 8747, + 'iota': 953, + 'iquest': 191, + 'isin': 8712, + 'iuml': 239, + 'kappa': 954, + 'lArr': 8656, + 'lambda': 955, + 'lang': 9001, + 'laquo': 171, + 'larr': 8592, + 'lceil': 8968, + 'ldquo': 8220, + 'le': 8804, + 'lfloor': 8970, + 'lowast': 8727, + 'loz': 9674, + 'lrm': 8206, + 'lsaquo': 8249, + 'lsquo': 8216, + 'lt': 60, + 'macr': 175, + 'mdash': 8212, + 'micro': 181, + 'middot': 183, + 'minus': 8722, + 'mu': 956, + 'nabla': 8711, + 'nbsp': 160, + 'ndash': 8211, + 'ne': 8800, + 'ni': 8715, + 'not': 172, + 'notin': 8713, + 'nsub': 8836, + 'ntilde': 241, + 'nu': 957, + 'oacute': 243, + 'ocirc': 244, + 'oelig': 339, + 'ograve': 242, + 'oline': 8254, + 'omega': 969, + 'omicron': 959, + 'oplus': 8853, + 'or': 8744, + 'ordf': 170, + 'ordm': 186, + 'oslash': 248, + 'otilde': 245, + 'otimes': 8855, + 'ouml': 246, + 'para': 182, + 'part': 8706, + 'permil': 8240, + 'perp': 8869, + 'phi': 966, + 'pi': 960, + 'piv': 982, + 'plusmn': 177, + 'pound': 163, + 'prime': 8242, + 'prod': 8719, + 'prop': 8733, + 'psi': 968, + 'quot': 34, + 'rArr': 8658, + 'radic': 8730, + 'rang': 9002, + 'raquo': 187, + 'rarr': 8594, + 'rceil': 8969, + 'rdquo': 8221, + 'real': 8476, + 'reg': 174, + 'rfloor': 8971, + 'rho': 961, + 'rlm': 8207, + 'rsaquo': 8250, + 'rsquo': 8217, + 'sbquo': 8218, + 'scaron': 353, + 'sdot': 8901, + 'sect': 167, + 'shy': 173, + 'sigma': 963, + 'sigmaf': 962, + 'sim': 8764, + 'spades': 9824, + 'sub': 8834, + 'sube': 8838, + 'sum': 8721, + 'sup': 8835, + 'sup1': 185, + 'sup2': 178, + 'sup3': 179, + 'supe': 8839, + 'szlig': 223, + 'tau': 964, + 'there4': 8756, + 'theta': 952, + 'thetasym': 977, + 'thinsp': 8201, + 'thorn': 254, + 'tilde': 732, + 'times': 215, + 'trade': 8482, + 'uArr': 8657, + 'uacute': 250, + 'uarr': 8593, + 'ucirc': 251, + 'ugrave': 249, + 'uml': 168, + 'upsih': 978, + 'upsilon': 965, + 'uuml': 252, + 'weierp': 8472, + 'xi': 958, + 'yacute': 253, + 'yen': 165, + 'yuml': 255, + 'zeta': 950, + 'zwj': 8205, + 'zwnj': 8204 +} diff --git a/mojo/public/third_party/markupsafe/_native.py b/mojo/public/third_party/markupsafe/_native.py new file mode 100644 index 0000000..5e83f10 --- /dev/null +++ b/mojo/public/third_party/markupsafe/_native.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +""" + markupsafe._native + ~~~~~~~~~~~~~~~~~~ + + Native Python implementation the C module is not compiled. + + :copyright: (c) 2010 by Armin Ronacher. + :license: BSD, see LICENSE for more details. +""" +from markupsafe import Markup +from markupsafe._compat import text_type + + +def escape(s): + """Convert the characters &, <, >, ' and " in string s to HTML-safe + sequences. Use this if you need to display text that might contain + such characters in HTML. Marks return value as markup string. + """ + if hasattr(s, '__html__'): + return s.__html__() + return Markup(text_type(s) + .replace('&', '&') + .replace('>', '>') + .replace('<', '<') + .replace("'", ''') + .replace('"', '"') + ) + + +def escape_silent(s): + """Like :func:`escape` but converts `None` into an empty + markup string. + """ + if s is None: + return Markup() + return escape(s) + + +def soft_unicode(s): + """Make a string unicode if it isn't already. That way a markup + string is not converted back to unicode. + """ + if not isinstance(s, text_type): + s = text_type(s) + return s diff --git a/mojo/public/third_party/markupsafe/_speedups.c b/mojo/public/third_party/markupsafe/_speedups.c new file mode 100644 index 0000000..f349feb --- /dev/null +++ b/mojo/public/third_party/markupsafe/_speedups.c @@ -0,0 +1,239 @@ +/** + * markupsafe._speedups + * ~~~~~~~~~~~~~~~~~~~~ + * + * This module implements functions for automatic escaping in C for better + * performance. + * + * :copyright: (c) 2010 by Armin Ronacher. + * :license: BSD. + */ + +#include <Python.h> + +#define ESCAPED_CHARS_TABLE_SIZE 63 +#define UNICHR(x) (PyUnicode_AS_UNICODE((PyUnicodeObject*)PyUnicode_DecodeASCII(x, strlen(x), NULL))); + +#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN) +typedef int Py_ssize_t; +#define PY_SSIZE_T_MAX INT_MAX +#define PY_SSIZE_T_MIN INT_MIN +#endif + + +static PyObject* markup; +static Py_ssize_t escaped_chars_delta_len[ESCAPED_CHARS_TABLE_SIZE]; +static Py_UNICODE *escaped_chars_repl[ESCAPED_CHARS_TABLE_SIZE]; + +static int +init_constants(void) +{ + PyObject *module; + /* happing of characters to replace */ + escaped_chars_repl['"'] = UNICHR("""); + escaped_chars_repl['\''] = UNICHR("'"); + escaped_chars_repl['&'] = UNICHR("&"); + escaped_chars_repl['<'] = UNICHR("<"); + escaped_chars_repl['>'] = UNICHR(">"); + + /* lengths of those characters when replaced - 1 */ + memset(escaped_chars_delta_len, 0, sizeof (escaped_chars_delta_len)); + escaped_chars_delta_len['"'] = escaped_chars_delta_len['\''] = \ + escaped_chars_delta_len['&'] = 4; + escaped_chars_delta_len['<'] = escaped_chars_delta_len['>'] = 3; + + /* import markup type so that we can mark the return value */ + module = PyImport_ImportModule("markupsafe"); + if (!module) + return 0; + markup = PyObject_GetAttrString(module, "Markup"); + Py_DECREF(module); + + return 1; +} + +static PyObject* +escape_unicode(PyUnicodeObject *in) +{ + PyUnicodeObject *out; + Py_UNICODE *inp = PyUnicode_AS_UNICODE(in); + const Py_UNICODE *inp_end = PyUnicode_AS_UNICODE(in) + PyUnicode_GET_SIZE(in); + Py_UNICODE *next_escp; + Py_UNICODE *outp; + Py_ssize_t delta=0, erepl=0, delta_len=0; + + /* First we need to figure out how long the escaped string will be */ + while (*(inp) || inp < inp_end) { + if (*inp < ESCAPED_CHARS_TABLE_SIZE) { + delta += escaped_chars_delta_len[*inp]; + erepl += !!escaped_chars_delta_len[*inp]; + } + ++inp; + } + + /* Do we need to escape anything at all? */ + if (!erepl) { + Py_INCREF(in); + return (PyObject*)in; + } + + out = (PyUnicodeObject*)PyUnicode_FromUnicode(NULL, PyUnicode_GET_SIZE(in) + delta); + if (!out) + return NULL; + + outp = PyUnicode_AS_UNICODE(out); + inp = PyUnicode_AS_UNICODE(in); + while (erepl-- > 0) { + /* look for the next substitution */ + next_escp = inp; + while (next_escp < inp_end) { + if (*next_escp < ESCAPED_CHARS_TABLE_SIZE && + (delta_len = escaped_chars_delta_len[*next_escp])) { + ++delta_len; + break; + } + ++next_escp; + } + + if (next_escp > inp) { + /* copy unescaped chars between inp and next_escp */ + Py_UNICODE_COPY(outp, inp, next_escp-inp); + outp += next_escp - inp; + } + + /* escape 'next_escp' */ + Py_UNICODE_COPY(outp, escaped_chars_repl[*next_escp], delta_len); + outp += delta_len; + + inp = next_escp + 1; + } + if (inp < inp_end) + Py_UNICODE_COPY(outp, inp, PyUnicode_GET_SIZE(in) - (inp - PyUnicode_AS_UNICODE(in))); + + return (PyObject*)out; +} + + +static PyObject* +escape(PyObject *self, PyObject *text) +{ + PyObject *s = NULL, *rv = NULL, *html; + + /* we don't have to escape integers, bools or floats */ + if (PyLong_CheckExact(text) || +#if PY_MAJOR_VERSION < 3 + PyInt_CheckExact(text) || +#endif + PyFloat_CheckExact(text) || PyBool_Check(text) || + text == Py_None) + return PyObject_CallFunctionObjArgs(markup, text, NULL); + + /* if the object has an __html__ method that performs the escaping */ + html = PyObject_GetAttrString(text, "__html__"); + if (html) { + rv = PyObject_CallObject(html, NULL); + Py_DECREF(html); + return rv; + } + + /* otherwise make the object unicode if it isn't, then escape */ + PyErr_Clear(); + if (!PyUnicode_Check(text)) { +#if PY_MAJOR_VERSION < 3 + PyObject *unicode = PyObject_Unicode(text); +#else + PyObject *unicode = PyObject_Str(text); +#endif + if (!unicode) + return NULL; + s = escape_unicode((PyUnicodeObject*)unicode); + Py_DECREF(unicode); + } + else + s = escape_unicode((PyUnicodeObject*)text); + + /* convert the unicode string into a markup object. */ + rv = PyObject_CallFunctionObjArgs(markup, (PyObject*)s, NULL); + Py_DECREF(s); + return rv; +} + + +static PyObject* +escape_silent(PyObject *self, PyObject *text) +{ + if (text != Py_None) + return escape(self, text); + return PyObject_CallFunctionObjArgs(markup, NULL); +} + + +static PyObject* +soft_unicode(PyObject *self, PyObject *s) +{ + if (!PyUnicode_Check(s)) +#if PY_MAJOR_VERSION < 3 + return PyObject_Unicode(s); +#else + return PyObject_Str(s); +#endif + Py_INCREF(s); + return s; +} + + +static PyMethodDef module_methods[] = { + {"escape", (PyCFunction)escape, METH_O, + "escape(s) -> markup\n\n" + "Convert the characters &, <, >, ', and \" in string s to HTML-safe\n" + "sequences. Use this if you need to display text that might contain\n" + "such characters in HTML. Marks return value as markup string."}, + {"escape_silent", (PyCFunction)escape_silent, METH_O, + "escape_silent(s) -> markup\n\n" + "Like escape but converts None to an empty string."}, + {"soft_unicode", (PyCFunction)soft_unicode, METH_O, + "soft_unicode(object) -> string\n\n" + "Make a string unicode if it isn't already. That way a markup\n" + "string is not converted back to unicode."}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + + +#if PY_MAJOR_VERSION < 3 + +#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ +#define PyMODINIT_FUNC void +#endif +PyMODINIT_FUNC +init_speedups(void) +{ + if (!init_constants()) + return; + + Py_InitModule3("markupsafe._speedups", module_methods, ""); +} + +#else /* Python 3.x module initialization */ + +static struct PyModuleDef module_definition = { + PyModuleDef_HEAD_INIT, + "markupsafe._speedups", + NULL, + -1, + module_methods, + NULL, + NULL, + NULL, + NULL +}; + +PyMODINIT_FUNC +PyInit__speedups(void) +{ + if (!init_constants()) + return NULL; + + return PyModule_Create(&module_definition); +} + +#endif diff --git a/mojo/public/third_party/markupsafe/get_markupsafe.sh b/mojo/public/third_party/markupsafe/get_markupsafe.sh new file mode 100755 index 0000000..d268832 --- /dev/null +++ b/mojo/public/third_party/markupsafe/get_markupsafe.sh @@ -0,0 +1,121 @@ +#!/bin/bash +# Download and extract MarkupSafe +# Homepage: +# https://github.com/mitsuhiko/markupsafe +# Download page: +# https://pypi.python.org/pypi/MarkupSafe +PACKAGE='MarkupSafe' +VERSION='0.18' +PACKAGE_DIR='markupsafe' + +CHROMIUM_FILES="README.chromium OWNERS get_markupsafe.sh" +EXTRA_FILES='LICENSE AUTHORS' +REMOVE_FILES='tests.py' + +SRC_URL='https://pypi.python.org/packages/source/' +SRC_URL+="${PACKAGE:0:1}/$PACKAGE/$PACKAGE-$VERSION.tar.gz" +FILENAME="$(basename $SRC_URL)" +MD5_FILENAME="$FILENAME.md5" +SHA512_FILENAME="$FILENAME.sha512" +CHROMIUM_FILES+=" $MD5_FILENAME $SHA512_FILENAME" + +BUILD_DIR="$PACKAGE-$VERSION" +THIRD_PARTY="$(dirname $(realpath $(dirname "${BASH_SOURCE[0]}")))" +INSTALL_DIR="$THIRD_PARTY/$PACKAGE_DIR" +OUT_DIR="$INSTALL_DIR/$BUILD_DIR/$PACKAGE_DIR" +OLD_DIR="$THIRD_PARTY/$PACKAGE_DIR.old" + +function check_hashes { + # Hashes generated via: + # FILENAME=MarkupSafe-0.18.tar.gz + # md5sum "$FILENAME" > "$FILENAME.md5" + # sha512sum "$FILENAME" > "$FILENAME.sha512" + # unset FILENAME + + # MD5 + if ! [ -f "$MD5_FILENAME" ] + then + echo "MD5 hash file $MD5_FILENAME not found, could not verify archive" + exit 1 + fi + + # 32-digit hash, followed by filename + MD5_HASHFILE_REGEX="^[0-9a-f]{32} $FILENAME" + if ! grep --extended-regex --line-regex --silent \ + "$MD5_HASHFILE_REGEX" "$MD5_FILENAME" + then + echo "MD5 hash file $MD5_FILENAME does not contain hash for $FILENAME," \ + 'could not verify archive' + echo 'Hash file contents are:' + cat "$MD5_FILENAME" + exit 1 + fi + + if ! md5sum --check "$MD5_FILENAME" + then + echo 'MD5 hash does not match,' \ + "archive file $FILENAME corrupt or compromised!" + exit 1 + fi + + # SHA-512 + if ! [ -f "$SHA512_FILENAME" ] + then + echo "SHA-512 hash file $SHA512_FILENAME not found," \ + 'could not verify archive' + exit 1 + fi + + # 128-digit hash, followed by filename + SHA512_HASHFILE_REGEX="^[0-9a-f]{128} $FILENAME" + if ! grep --extended-regex --line-regex --silent \ + "$SHA512_HASHFILE_REGEX" "$SHA512_FILENAME" + then + echo "SHA-512 hash file $SHA512_FILENAME does not contain hash for" \ + "$FILENAME, could not verify archive" + echo 'Hash file contents are:' + cat "$SHA512_FILENAME" + exit 1 + fi + + if ! sha512sum --check "$SHA512_FILENAME" + then + echo 'SHA-512 hash does not match,' \ + "archive file $FILENAME corrupt or compromised!" + exit 1 + fi +} + + +################################################################################ +# Body + +cd "$INSTALL_DIR" +echo "Downloading $SRC_URL" +curl --remote-name "$SRC_URL" +check_hashes +tar xvzf "$FILENAME" +# Copy extra files over +for FILE in $CHROMIUM_FILES +do + cp "$FILE" "$OUT_DIR" +done + +cd "$BUILD_DIR" +for FILE in $EXTRA_FILES +do + cp "$FILE" "$OUT_DIR" +done + +cd "$OUT_DIR" +for FILE in $REMOVE_FILES +do + rm -fr "$FILE" +done + +# Replace with new directory +cd .. +mv "$INSTALL_DIR" "$OLD_DIR" +mv "$PACKAGE_DIR" "$INSTALL_DIR" +cd "$INSTALL_DIR" +rm -fr "$OLD_DIR" diff --git a/mojo/public/third_party/ply/LICENSE b/mojo/public/third_party/ply/LICENSE new file mode 100644 index 0000000..b273866 --- /dev/null +++ b/mojo/public/third_party/ply/LICENSE @@ -0,0 +1,30 @@ +PLY (Python Lex-Yacc) Version 3.4 + +Copyright (C) 2001-2011, +David M. Beazley (Dabeaz LLC) +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +* Neither the name of the David Beazley or Dabeaz LLC may be used to + endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file diff --git a/mojo/public/third_party/ply/README b/mojo/public/third_party/ply/README new file mode 100644 index 0000000..f384d1a --- /dev/null +++ b/mojo/public/third_party/ply/README @@ -0,0 +1,271 @@ +PLY (Python Lex-Yacc) Version 3.4 + +Copyright (C) 2001-2011, +David M. Beazley (Dabeaz LLC) +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +* Neither the name of the David Beazley or Dabeaz LLC may be used to + endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Introduction +============ + +PLY is a 100% Python implementation of the common parsing tools lex +and yacc. Here are a few highlights: + + - PLY is very closely modeled after traditional lex/yacc. + If you know how to use these tools in C, you will find PLY + to be similar. + + - PLY provides *very* extensive error reporting and diagnostic + information to assist in parser construction. The original + implementation was developed for instructional purposes. As + a result, the system tries to identify the most common types + of errors made by novice users. + + - PLY provides full support for empty productions, error recovery, + precedence specifiers, and moderately ambiguous grammars. + + - Parsing is based on LR-parsing which is fast, memory efficient, + better suited to large grammars, and which has a number of nice + properties when dealing with syntax errors and other parsing problems. + Currently, PLY builds its parsing tables using the LALR(1) + algorithm used in yacc. + + - PLY uses Python introspection features to build lexers and parsers. + This greatly simplifies the task of parser construction since it reduces + the number of files and eliminates the need to run a separate lex/yacc + tool before running your program. + + - PLY can be used to build parsers for "real" programming languages. + Although it is not ultra-fast due to its Python implementation, + PLY can be used to parse grammars consisting of several hundred + rules (as might be found for a language like C). The lexer and LR + parser are also reasonably efficient when parsing typically + sized programs. People have used PLY to build parsers for + C, C++, ADA, and other real programming languages. + +How to Use +========== + +PLY consists of two files : lex.py and yacc.py. These are contained +within the 'ply' directory which may also be used as a Python package. +To use PLY, simply copy the 'ply' directory to your project and import +lex and yacc from the associated 'ply' package. For example: + + import ply.lex as lex + import ply.yacc as yacc + +Alternatively, you can copy just the files lex.py and yacc.py +individually and use them as modules. For example: + + import lex + import yacc + +The file setup.py can be used to install ply using distutils. + +The file doc/ply.html contains complete documentation on how to use +the system. + +The example directory contains several different examples including a +PLY specification for ANSI C as given in K&R 2nd Ed. + +A simple example is found at the end of this document + +Requirements +============ +PLY requires the use of Python 2.2 or greater. However, you should +use the latest Python release if possible. It should work on just +about any platform. PLY has been tested with both CPython and Jython. +It also seems to work with IronPython. + +Resources +========= +More information about PLY can be obtained on the PLY webpage at: + + http://www.dabeaz.com/ply + +For a detailed overview of parsing theory, consult the excellent +book "Compilers : Principles, Techniques, and Tools" by Aho, Sethi, and +Ullman. The topics found in "Lex & Yacc" by Levine, Mason, and Brown +may also be useful. + +A Google group for PLY can be found at + + http://groups.google.com/group/ply-hack + +Acknowledgments +=============== +A special thanks is in order for all of the students in CS326 who +suffered through about 25 different versions of these tools :-). + +The CHANGES file acknowledges those who have contributed patches. + +Elias Ioup did the first implementation of LALR(1) parsing in PLY-1.x. +Andrew Waters and Markus Schoepflin were instrumental in reporting bugs +and testing a revised LALR(1) implementation for PLY-2.0. + +Special Note for PLY-3.0 +======================== +PLY-3.0 the first PLY release to support Python 3. However, backwards +compatibility with Python 2.2 is still preserved. PLY provides dual +Python 2/3 compatibility by restricting its implementation to a common +subset of basic language features. You should not convert PLY using +2to3--it is not necessary and may in fact break the implementation. + +Example +======= + +Here is a simple example showing a PLY implementation of a calculator +with variables. + +# ----------------------------------------------------------------------------- +# calc.py +# +# A simple calculator with variables. +# ----------------------------------------------------------------------------- + +tokens = ( + 'NAME','NUMBER', + 'PLUS','MINUS','TIMES','DIVIDE','EQUALS', + 'LPAREN','RPAREN', + ) + +# Tokens + +t_PLUS = r'\+' +t_MINUS = r'-' +t_TIMES = r'\*' +t_DIVIDE = r'/' +t_EQUALS = r'=' +t_LPAREN = r'\(' +t_RPAREN = r'\)' +t_NAME = r'[a-zA-Z_][a-zA-Z0-9_]*' + +def t_NUMBER(t): + r'\d+' + t.value = int(t.value) + return t + +# Ignored characters +t_ignore = " \t" + +def t_newline(t): + r'\n+' + t.lexer.lineno += t.value.count("\n") + +def t_error(t): + print("Illegal character '%s'" % t.value[0]) + t.lexer.skip(1) + +# Build the lexer +import ply.lex as lex +lex.lex() + +# Precedence rules for the arithmetic operators +precedence = ( + ('left','PLUS','MINUS'), + ('left','TIMES','DIVIDE'), + ('right','UMINUS'), + ) + +# dictionary of names (for storing variables) +names = { } + +def p_statement_assign(p): + 'statement : NAME EQUALS expression' + names[p[1]] = p[3] + +def p_statement_expr(p): + 'statement : expression' + print(p[1]) + +def p_expression_binop(p): + '''expression : expression PLUS expression + | expression MINUS expression + | expression TIMES expression + | expression DIVIDE expression''' + if p[2] == '+' : p[0] = p[1] + p[3] + elif p[2] == '-': p[0] = p[1] - p[3] + elif p[2] == '*': p[0] = p[1] * p[3] + elif p[2] == '/': p[0] = p[1] / p[3] + +def p_expression_uminus(p): + 'expression : MINUS expression %prec UMINUS' + p[0] = -p[2] + +def p_expression_group(p): + 'expression : LPAREN expression RPAREN' + p[0] = p[2] + +def p_expression_number(p): + 'expression : NUMBER' + p[0] = p[1] + +def p_expression_name(p): + 'expression : NAME' + try: + p[0] = names[p[1]] + except LookupError: + print("Undefined name '%s'" % p[1]) + p[0] = 0 + +def p_error(p): + print("Syntax error at '%s'" % p.value) + +import ply.yacc as yacc +yacc.yacc() + +while 1: + try: + s = raw_input('calc > ') # use input() on Python 3 + except EOFError: + break + yacc.parse(s) + + +Bug Reports and Patches +======================= +My goal with PLY is to simply have a decent lex/yacc implementation +for Python. As a general rule, I don't spend huge amounts of time +working on it unless I receive very specific bug reports and/or +patches to fix problems. I also try to incorporate submitted feature +requests and enhancements into each new version. To contact me about +bugs and/or new features, please send email to dave@dabeaz.com. + +In addition there is a Google group for discussing PLY related issues at + + http://groups.google.com/group/ply-hack + +-- Dave + + + + + + + + + diff --git a/mojo/public/third_party/ply/README.chromium b/mojo/public/third_party/ply/README.chromium new file mode 100644 index 0000000..6466e54 --- /dev/null +++ b/mojo/public/third_party/ply/README.chromium @@ -0,0 +1,21 @@ +Name: PLY (Python Lex-Yacc) +Current version: 3.4 +URL: http://www.dabeaz.com/ply/ply-3.4.tar.gz +License: BSD +License File: LICENSE +Security Critical: no +Version: 3.4 + +This directory contains a copy of these ply-3.4 components: + +README ply-3.4/README +Sources ply-3.4/ply/__init__.py + ply-3.4/ply/lex.py + ply-3.4/ply/yacc.py + + +The license is in LICENSE. + +Modifications made with initial commit: +- Added the file README.chromium (this file) +- Applies license.patch diff --git a/mojo/public/third_party/ply/__init__.py b/mojo/public/third_party/ply/__init__.py new file mode 100644 index 0000000..f3da03e --- /dev/null +++ b/mojo/public/third_party/ply/__init__.py @@ -0,0 +1,36 @@ +# PLY package +# Author: David Beazley (dave@dabeaz.com) +# ----------------------------------------------------------------------------- +# ply: yacc.py +# +# Copyright (C) 2001-2011, +# David M. Beazley (Dabeaz LLC) +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of the David Beazley or Dabeaz LLC may be used to +# endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# ----------------------------------------------------------------------------- + +__all__ = ['lex','yacc'] diff --git a/mojo/public/third_party/ply/lex.py b/mojo/public/third_party/ply/lex.py new file mode 100644 index 0000000..bd32da9 --- /dev/null +++ b/mojo/public/third_party/ply/lex.py @@ -0,0 +1,1058 @@ +# ----------------------------------------------------------------------------- +# ply: lex.py +# +# Copyright (C) 2001-2011, +# David M. Beazley (Dabeaz LLC) +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of the David Beazley or Dabeaz LLC may be used to +# endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# ----------------------------------------------------------------------------- + +__version__ = "3.4" +__tabversion__ = "3.2" # Version of table file used + +import re, sys, types, copy, os + +# This tuple contains known string types +try: + # Python 2.6 + StringTypes = (types.StringType, types.UnicodeType) +except AttributeError: + # Python 3.0 + StringTypes = (str, bytes) + +# Extract the code attribute of a function. Different implementations +# are for Python 2/3 compatibility. + +if sys.version_info[0] < 3: + def func_code(f): + return f.func_code +else: + def func_code(f): + return f.__code__ + +# This regular expression is used to match valid token names +_is_identifier = re.compile(r'^[a-zA-Z0-9_]+$') + +# Exception thrown when invalid token encountered and no default error +# handler is defined. + +class LexError(Exception): + def __init__(self,message,s): + self.args = (message,) + self.text = s + +# Token class. This class is used to represent the tokens produced. +class LexToken(object): + def __str__(self): + return "LexToken(%s,%r,%d,%d)" % (self.type,self.value,self.lineno,self.lexpos) + def __repr__(self): + return str(self) + +# This object is a stand-in for a logging object created by the +# logging module. + +class PlyLogger(object): + def __init__(self,f): + self.f = f + def critical(self,msg,*args,**kwargs): + self.f.write((msg % args) + "\n") + + def warning(self,msg,*args,**kwargs): + self.f.write("WARNING: "+ (msg % args) + "\n") + + def error(self,msg,*args,**kwargs): + self.f.write("ERROR: " + (msg % args) + "\n") + + info = critical + debug = critical + +# Null logger is used when no output is generated. Does nothing. +class NullLogger(object): + def __getattribute__(self,name): + return self + def __call__(self,*args,**kwargs): + return self + +# ----------------------------------------------------------------------------- +# === Lexing Engine === +# +# The following Lexer class implements the lexer runtime. There are only +# a few public methods and attributes: +# +# input() - Store a new string in the lexer +# token() - Get the next token +# clone() - Clone the lexer +# +# lineno - Current line number +# lexpos - Current position in the input string +# ----------------------------------------------------------------------------- + +class Lexer: + def __init__(self): + self.lexre = None # Master regular expression. This is a list of + # tuples (re,findex) where re is a compiled + # regular expression and findex is a list + # mapping regex group numbers to rules + self.lexretext = None # Current regular expression strings + self.lexstatere = {} # Dictionary mapping lexer states to master regexs + self.lexstateretext = {} # Dictionary mapping lexer states to regex strings + self.lexstaterenames = {} # Dictionary mapping lexer states to symbol names + self.lexstate = "INITIAL" # Current lexer state + self.lexstatestack = [] # Stack of lexer states + self.lexstateinfo = None # State information + self.lexstateignore = {} # Dictionary of ignored characters for each state + self.lexstateerrorf = {} # Dictionary of error functions for each state + self.lexreflags = 0 # Optional re compile flags + self.lexdata = None # Actual input data (as a string) + self.lexpos = 0 # Current position in input text + self.lexlen = 0 # Length of the input text + self.lexerrorf = None # Error rule (if any) + self.lextokens = None # List of valid tokens + self.lexignore = "" # Ignored characters + self.lexliterals = "" # Literal characters that can be passed through + self.lexmodule = None # Module + self.lineno = 1 # Current line number + self.lexoptimize = 0 # Optimized mode + + def clone(self,object=None): + c = copy.copy(self) + + # If the object parameter has been supplied, it means we are attaching the + # lexer to a new object. In this case, we have to rebind all methods in + # the lexstatere and lexstateerrorf tables. + + if object: + newtab = { } + for key, ritem in self.lexstatere.items(): + newre = [] + for cre, findex in ritem: + newfindex = [] + for f in findex: + if not f or not f[0]: + newfindex.append(f) + continue + newfindex.append((getattr(object,f[0].__name__),f[1])) + newre.append((cre,newfindex)) + newtab[key] = newre + c.lexstatere = newtab + c.lexstateerrorf = { } + for key, ef in self.lexstateerrorf.items(): + c.lexstateerrorf[key] = getattr(object,ef.__name__) + c.lexmodule = object + return c + + # ------------------------------------------------------------ + # writetab() - Write lexer information to a table file + # ------------------------------------------------------------ + def writetab(self,tabfile,outputdir=""): + if isinstance(tabfile,types.ModuleType): + return + basetabfilename = tabfile.split(".")[-1] + filename = os.path.join(outputdir,basetabfilename)+".py" + tf = open(filename,"w") + tf.write("# %s.py. This file automatically created by PLY (version %s). Don't edit!\n" % (tabfile,__version__)) + tf.write("_tabversion = %s\n" % repr(__version__)) + tf.write("_lextokens = %s\n" % repr(self.lextokens)) + tf.write("_lexreflags = %s\n" % repr(self.lexreflags)) + tf.write("_lexliterals = %s\n" % repr(self.lexliterals)) + tf.write("_lexstateinfo = %s\n" % repr(self.lexstateinfo)) + + tabre = { } + # Collect all functions in the initial state + initial = self.lexstatere["INITIAL"] + initialfuncs = [] + for part in initial: + for f in part[1]: + if f and f[0]: + initialfuncs.append(f) + + for key, lre in self.lexstatere.items(): + titem = [] + for i in range(len(lre)): + titem.append((self.lexstateretext[key][i],_funcs_to_names(lre[i][1],self.lexstaterenames[key][i]))) + tabre[key] = titem + + tf.write("_lexstatere = %s\n" % repr(tabre)) + tf.write("_lexstateignore = %s\n" % repr(self.lexstateignore)) + + taberr = { } + for key, ef in self.lexstateerrorf.items(): + if ef: + taberr[key] = ef.__name__ + else: + taberr[key] = None + tf.write("_lexstateerrorf = %s\n" % repr(taberr)) + tf.close() + + # ------------------------------------------------------------ + # readtab() - Read lexer information from a tab file + # ------------------------------------------------------------ + def readtab(self,tabfile,fdict): + if isinstance(tabfile,types.ModuleType): + lextab = tabfile + else: + if sys.version_info[0] < 3: + exec("import %s as lextab" % tabfile) + else: + env = { } + exec("import %s as lextab" % tabfile, env,env) + lextab = env['lextab'] + + if getattr(lextab,"_tabversion","0.0") != __version__: + raise ImportError("Inconsistent PLY version") + + self.lextokens = lextab._lextokens + self.lexreflags = lextab._lexreflags + self.lexliterals = lextab._lexliterals + self.lexstateinfo = lextab._lexstateinfo + self.lexstateignore = lextab._lexstateignore + self.lexstatere = { } + self.lexstateretext = { } + for key,lre in lextab._lexstatere.items(): + titem = [] + txtitem = [] + for i in range(len(lre)): + titem.append((re.compile(lre[i][0],lextab._lexreflags | re.VERBOSE),_names_to_funcs(lre[i][1],fdict))) + txtitem.append(lre[i][0]) + self.lexstatere[key] = titem + self.lexstateretext[key] = txtitem + self.lexstateerrorf = { } + for key,ef in lextab._lexstateerrorf.items(): + self.lexstateerrorf[key] = fdict[ef] + self.begin('INITIAL') + + # ------------------------------------------------------------ + # input() - Push a new string into the lexer + # ------------------------------------------------------------ + def input(self,s): + # Pull off the first character to see if s looks like a string + c = s[:1] + if not isinstance(c,StringTypes): + raise ValueError("Expected a string") + self.lexdata = s + self.lexpos = 0 + self.lexlen = len(s) + + # ------------------------------------------------------------ + # begin() - Changes the lexing state + # ------------------------------------------------------------ + def begin(self,state): + if not state in self.lexstatere: + raise ValueError("Undefined state") + self.lexre = self.lexstatere[state] + self.lexretext = self.lexstateretext[state] + self.lexignore = self.lexstateignore.get(state,"") + self.lexerrorf = self.lexstateerrorf.get(state,None) + self.lexstate = state + + # ------------------------------------------------------------ + # push_state() - Changes the lexing state and saves old on stack + # ------------------------------------------------------------ + def push_state(self,state): + self.lexstatestack.append(self.lexstate) + self.begin(state) + + # ------------------------------------------------------------ + # pop_state() - Restores the previous state + # ------------------------------------------------------------ + def pop_state(self): + self.begin(self.lexstatestack.pop()) + + # ------------------------------------------------------------ + # current_state() - Returns the current lexing state + # ------------------------------------------------------------ + def current_state(self): + return self.lexstate + + # ------------------------------------------------------------ + # skip() - Skip ahead n characters + # ------------------------------------------------------------ + def skip(self,n): + self.lexpos += n + + # ------------------------------------------------------------ + # opttoken() - Return the next token from the Lexer + # + # Note: This function has been carefully implemented to be as fast + # as possible. Don't make changes unless you really know what + # you are doing + # ------------------------------------------------------------ + def token(self): + # Make local copies of frequently referenced attributes + lexpos = self.lexpos + lexlen = self.lexlen + lexignore = self.lexignore + lexdata = self.lexdata + + while lexpos < lexlen: + # This code provides some short-circuit code for whitespace, tabs, and other ignored characters + if lexdata[lexpos] in lexignore: + lexpos += 1 + continue + + # Look for a regular expression match + for lexre,lexindexfunc in self.lexre: + m = lexre.match(lexdata,lexpos) + if not m: continue + + # Create a token for return + tok = LexToken() + tok.value = m.group() + tok.lineno = self.lineno + tok.lexpos = lexpos + + i = m.lastindex + func,tok.type = lexindexfunc[i] + + if not func: + # If no token type was set, it's an ignored token + if tok.type: + self.lexpos = m.end() + return tok + else: + lexpos = m.end() + break + + lexpos = m.end() + + # If token is processed by a function, call it + + tok.lexer = self # Set additional attributes useful in token rules + self.lexmatch = m + self.lexpos = lexpos + + newtok = func(tok) + + # Every function must return a token, if nothing, we just move to next token + if not newtok: + lexpos = self.lexpos # This is here in case user has updated lexpos. + lexignore = self.lexignore # This is here in case there was a state change + break + + # Verify type of the token. If not in the token map, raise an error + if not self.lexoptimize: + if not newtok.type in self.lextokens: + raise LexError("%s:%d: Rule '%s' returned an unknown token type '%s'" % ( + func_code(func).co_filename, func_code(func).co_firstlineno, + func.__name__, newtok.type),lexdata[lexpos:]) + + return newtok + else: + # No match, see if in literals + if lexdata[lexpos] in self.lexliterals: + tok = LexToken() + tok.value = lexdata[lexpos] + tok.lineno = self.lineno + tok.type = tok.value + tok.lexpos = lexpos + self.lexpos = lexpos + 1 + return tok + + # No match. Call t_error() if defined. + if self.lexerrorf: + tok = LexToken() + tok.value = self.lexdata[lexpos:] + tok.lineno = self.lineno + tok.type = "error" + tok.lexer = self + tok.lexpos = lexpos + self.lexpos = lexpos + newtok = self.lexerrorf(tok) + if lexpos == self.lexpos: + # Error method didn't change text position at all. This is an error. + raise LexError("Scanning error. Illegal character '%s'" % (lexdata[lexpos]), lexdata[lexpos:]) + lexpos = self.lexpos + if not newtok: continue + return newtok + + self.lexpos = lexpos + raise LexError("Illegal character '%s' at index %d" % (lexdata[lexpos],lexpos), lexdata[lexpos:]) + + self.lexpos = lexpos + 1 + if self.lexdata is None: + raise RuntimeError("No input string given with input()") + return None + + # Iterator interface + def __iter__(self): + return self + + def next(self): + t = self.token() + if t is None: + raise StopIteration + return t + + __next__ = next + +# ----------------------------------------------------------------------------- +# ==== Lex Builder === +# +# The functions and classes below are used to collect lexing information +# and build a Lexer object from it. +# ----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- +# get_caller_module_dict() +# +# This function returns a dictionary containing all of the symbols defined within +# a caller further down the call stack. This is used to get the environment +# associated with the yacc() call if none was provided. +# ----------------------------------------------------------------------------- + +def get_caller_module_dict(levels): + try: + raise RuntimeError + except RuntimeError: + e,b,t = sys.exc_info() + f = t.tb_frame + while levels > 0: + f = f.f_back + levels -= 1 + ldict = f.f_globals.copy() + if f.f_globals != f.f_locals: + ldict.update(f.f_locals) + + return ldict + +# ----------------------------------------------------------------------------- +# _funcs_to_names() +# +# Given a list of regular expression functions, this converts it to a list +# suitable for output to a table file +# ----------------------------------------------------------------------------- + +def _funcs_to_names(funclist,namelist): + result = [] + for f,name in zip(funclist,namelist): + if f and f[0]: + result.append((name, f[1])) + else: + result.append(f) + return result + +# ----------------------------------------------------------------------------- +# _names_to_funcs() +# +# Given a list of regular expression function names, this converts it back to +# functions. +# ----------------------------------------------------------------------------- + +def _names_to_funcs(namelist,fdict): + result = [] + for n in namelist: + if n and n[0]: + result.append((fdict[n[0]],n[1])) + else: + result.append(n) + return result + +# ----------------------------------------------------------------------------- +# _form_master_re() +# +# This function takes a list of all of the regex components and attempts to +# form the master regular expression. Given limitations in the Python re +# module, it may be necessary to break the master regex into separate expressions. +# ----------------------------------------------------------------------------- + +def _form_master_re(relist,reflags,ldict,toknames): + if not relist: return [] + regex = "|".join(relist) + try: + lexre = re.compile(regex,re.VERBOSE | reflags) + + # Build the index to function map for the matching engine + lexindexfunc = [ None ] * (max(lexre.groupindex.values())+1) + lexindexnames = lexindexfunc[:] + + for f,i in lexre.groupindex.items(): + handle = ldict.get(f,None) + if type(handle) in (types.FunctionType, types.MethodType): + lexindexfunc[i] = (handle,toknames[f]) + lexindexnames[i] = f + elif handle is not None: + lexindexnames[i] = f + if f.find("ignore_") > 0: + lexindexfunc[i] = (None,None) + else: + lexindexfunc[i] = (None, toknames[f]) + + return [(lexre,lexindexfunc)],[regex],[lexindexnames] + except Exception: + m = int(len(relist)/2) + if m == 0: m = 1 + llist, lre, lnames = _form_master_re(relist[:m],reflags,ldict,toknames) + rlist, rre, rnames = _form_master_re(relist[m:],reflags,ldict,toknames) + return llist+rlist, lre+rre, lnames+rnames + +# ----------------------------------------------------------------------------- +# def _statetoken(s,names) +# +# Given a declaration name s of the form "t_" and a dictionary whose keys are +# state names, this function returns a tuple (states,tokenname) where states +# is a tuple of state names and tokenname is the name of the token. For example, +# calling this with s = "t_foo_bar_SPAM" might return (('foo','bar'),'SPAM') +# ----------------------------------------------------------------------------- + +def _statetoken(s,names): + nonstate = 1 + parts = s.split("_") + for i in range(1,len(parts)): + if not parts[i] in names and parts[i] != 'ANY': break + if i > 1: + states = tuple(parts[1:i]) + else: + states = ('INITIAL',) + + if 'ANY' in states: + states = tuple(names) + + tokenname = "_".join(parts[i:]) + return (states,tokenname) + + +# ----------------------------------------------------------------------------- +# LexerReflect() +# +# This class represents information needed to build a lexer as extracted from a +# user's input file. +# ----------------------------------------------------------------------------- +class LexerReflect(object): + def __init__(self,ldict,log=None,reflags=0): + self.ldict = ldict + self.error_func = None + self.tokens = [] + self.reflags = reflags + self.stateinfo = { 'INITIAL' : 'inclusive'} + self.files = {} + self.error = 0 + + if log is None: + self.log = PlyLogger(sys.stderr) + else: + self.log = log + + # Get all of the basic information + def get_all(self): + self.get_tokens() + self.get_literals() + self.get_states() + self.get_rules() + + # Validate all of the information + def validate_all(self): + self.validate_tokens() + self.validate_literals() + self.validate_rules() + return self.error + + # Get the tokens map + def get_tokens(self): + tokens = self.ldict.get("tokens",None) + if not tokens: + self.log.error("No token list is defined") + self.error = 1 + return + + if not isinstance(tokens,(list, tuple)): + self.log.error("tokens must be a list or tuple") + self.error = 1 + return + + if not tokens: + self.log.error("tokens is empty") + self.error = 1 + return + + self.tokens = tokens + + # Validate the tokens + def validate_tokens(self): + terminals = {} + for n in self.tokens: + if not _is_identifier.match(n): + self.log.error("Bad token name '%s'",n) + self.error = 1 + if n in terminals: + self.log.warning("Token '%s' multiply defined", n) + terminals[n] = 1 + + # Get the literals specifier + def get_literals(self): + self.literals = self.ldict.get("literals","") + + # Validate literals + def validate_literals(self): + try: + for c in self.literals: + if not isinstance(c,StringTypes) or len(c) > 1: + self.log.error("Invalid literal %s. Must be a single character", repr(c)) + self.error = 1 + continue + + except TypeError: + self.log.error("Invalid literals specification. literals must be a sequence of characters") + self.error = 1 + + def get_states(self): + self.states = self.ldict.get("states",None) + # Build statemap + if self.states: + if not isinstance(self.states,(tuple,list)): + self.log.error("states must be defined as a tuple or list") + self.error = 1 + else: + for s in self.states: + if not isinstance(s,tuple) or len(s) != 2: + self.log.error("Invalid state specifier %s. Must be a tuple (statename,'exclusive|inclusive')",repr(s)) + self.error = 1 + continue + name, statetype = s + if not isinstance(name,StringTypes): + self.log.error("State name %s must be a string", repr(name)) + self.error = 1 + continue + if not (statetype == 'inclusive' or statetype == 'exclusive'): + self.log.error("State type for state %s must be 'inclusive' or 'exclusive'",name) + self.error = 1 + continue + if name in self.stateinfo: + self.log.error("State '%s' already defined",name) + self.error = 1 + continue + self.stateinfo[name] = statetype + + # Get all of the symbols with a t_ prefix and sort them into various + # categories (functions, strings, error functions, and ignore characters) + + def get_rules(self): + tsymbols = [f for f in self.ldict if f[:2] == 't_' ] + + # Now build up a list of functions and a list of strings + + self.toknames = { } # Mapping of symbols to token names + self.funcsym = { } # Symbols defined as functions + self.strsym = { } # Symbols defined as strings + self.ignore = { } # Ignore strings by state + self.errorf = { } # Error functions by state + + for s in self.stateinfo: + self.funcsym[s] = [] + self.strsym[s] = [] + + if len(tsymbols) == 0: + self.log.error("No rules of the form t_rulename are defined") + self.error = 1 + return + + for f in tsymbols: + t = self.ldict[f] + states, tokname = _statetoken(f,self.stateinfo) + self.toknames[f] = tokname + + if hasattr(t,"__call__"): + if tokname == 'error': + for s in states: + self.errorf[s] = t + elif tokname == 'ignore': + line = func_code(t).co_firstlineno + file = func_code(t).co_filename + self.log.error("%s:%d: Rule '%s' must be defined as a string",file,line,t.__name__) + self.error = 1 + else: + for s in states: + self.funcsym[s].append((f,t)) + elif isinstance(t, StringTypes): + if tokname == 'ignore': + for s in states: + self.ignore[s] = t + if "\\" in t: + self.log.warning("%s contains a literal backslash '\\'",f) + + elif tokname == 'error': + self.log.error("Rule '%s' must be defined as a function", f) + self.error = 1 + else: + for s in states: + self.strsym[s].append((f,t)) + else: + self.log.error("%s not defined as a function or string", f) + self.error = 1 + + # Sort the functions by line number + for f in self.funcsym.values(): + if sys.version_info[0] < 3: + f.sort(lambda x,y: cmp(func_code(x[1]).co_firstlineno,func_code(y[1]).co_firstlineno)) + else: + # Python 3.0 + f.sort(key=lambda x: func_code(x[1]).co_firstlineno) + + # Sort the strings by regular expression length + for s in self.strsym.values(): + if sys.version_info[0] < 3: + s.sort(lambda x,y: (len(x[1]) < len(y[1])) - (len(x[1]) > len(y[1]))) + else: + # Python 3.0 + s.sort(key=lambda x: len(x[1]),reverse=True) + + # Validate all of the t_rules collected + def validate_rules(self): + for state in self.stateinfo: + # Validate all rules defined by functions + + + + for fname, f in self.funcsym[state]: + line = func_code(f).co_firstlineno + file = func_code(f).co_filename + self.files[file] = 1 + + tokname = self.toknames[fname] + if isinstance(f, types.MethodType): + reqargs = 2 + else: + reqargs = 1 + nargs = func_code(f).co_argcount + if nargs > reqargs: + self.log.error("%s:%d: Rule '%s' has too many arguments",file,line,f.__name__) + self.error = 1 + continue + + if nargs < reqargs: + self.log.error("%s:%d: Rule '%s' requires an argument", file,line,f.__name__) + self.error = 1 + continue + + if not f.__doc__: + self.log.error("%s:%d: No regular expression defined for rule '%s'",file,line,f.__name__) + self.error = 1 + continue + + try: + c = re.compile("(?P<%s>%s)" % (fname,f.__doc__), re.VERBOSE | self.reflags) + if c.match(""): + self.log.error("%s:%d: Regular expression for rule '%s' matches empty string", file,line,f.__name__) + self.error = 1 + except re.error: + _etype, e, _etrace = sys.exc_info() + self.log.error("%s:%d: Invalid regular expression for rule '%s'. %s", file,line,f.__name__,e) + if '#' in f.__doc__: + self.log.error("%s:%d. Make sure '#' in rule '%s' is escaped with '\\#'",file,line, f.__name__) + self.error = 1 + + # Validate all rules defined by strings + for name,r in self.strsym[state]: + tokname = self.toknames[name] + if tokname == 'error': + self.log.error("Rule '%s' must be defined as a function", name) + self.error = 1 + continue + + if not tokname in self.tokens and tokname.find("ignore_") < 0: + self.log.error("Rule '%s' defined for an unspecified token %s",name,tokname) + self.error = 1 + continue + + try: + c = re.compile("(?P<%s>%s)" % (name,r),re.VERBOSE | self.reflags) + if (c.match("")): + self.log.error("Regular expression for rule '%s' matches empty string",name) + self.error = 1 + except re.error: + _etype, e, _etrace = sys.exc_info() + self.log.error("Invalid regular expression for rule '%s'. %s",name,e) + if '#' in r: + self.log.error("Make sure '#' in rule '%s' is escaped with '\\#'",name) + self.error = 1 + + if not self.funcsym[state] and not self.strsym[state]: + self.log.error("No rules defined for state '%s'",state) + self.error = 1 + + # Validate the error function + efunc = self.errorf.get(state,None) + if efunc: + f = efunc + line = func_code(f).co_firstlineno + file = func_code(f).co_filename + self.files[file] = 1 + + if isinstance(f, types.MethodType): + reqargs = 2 + else: + reqargs = 1 + nargs = func_code(f).co_argcount + if nargs > reqargs: + self.log.error("%s:%d: Rule '%s' has too many arguments",file,line,f.__name__) + self.error = 1 + + if nargs < reqargs: + self.log.error("%s:%d: Rule '%s' requires an argument", file,line,f.__name__) + self.error = 1 + + for f in self.files: + self.validate_file(f) + + + # ----------------------------------------------------------------------------- + # validate_file() + # + # This checks to see if there are duplicated t_rulename() functions or strings + # in the parser input file. This is done using a simple regular expression + # match on each line in the given file. + # ----------------------------------------------------------------------------- + + def validate_file(self,filename): + import os.path + base,ext = os.path.splitext(filename) + if ext != '.py': return # No idea what the file is. Return OK + + try: + f = open(filename) + lines = f.readlines() + f.close() + except IOError: + return # Couldn't find the file. Don't worry about it + + fre = re.compile(r'\s*def\s+(t_[a-zA-Z_0-9]*)\(') + sre = re.compile(r'\s*(t_[a-zA-Z_0-9]*)\s*=') + + counthash = { } + linen = 1 + for l in lines: + m = fre.match(l) + if not m: + m = sre.match(l) + if m: + name = m.group(1) + prev = counthash.get(name) + if not prev: + counthash[name] = linen + else: + self.log.error("%s:%d: Rule %s redefined. Previously defined on line %d",filename,linen,name,prev) + self.error = 1 + linen += 1 + +# ----------------------------------------------------------------------------- +# lex(module) +# +# Build all of the regular expression rules from definitions in the supplied module +# ----------------------------------------------------------------------------- +def lex(module=None,object=None,debug=0,optimize=0,lextab="lextab",reflags=0,nowarn=0,outputdir="", debuglog=None, errorlog=None): + global lexer + ldict = None + stateinfo = { 'INITIAL' : 'inclusive'} + lexobj = Lexer() + lexobj.lexoptimize = optimize + global token,input + + if errorlog is None: + errorlog = PlyLogger(sys.stderr) + + if debug: + if debuglog is None: + debuglog = PlyLogger(sys.stderr) + + # Get the module dictionary used for the lexer + if object: module = object + + if module: + _items = [(k,getattr(module,k)) for k in dir(module)] + ldict = dict(_items) + else: + ldict = get_caller_module_dict(2) + + # Collect parser information from the dictionary + linfo = LexerReflect(ldict,log=errorlog,reflags=reflags) + linfo.get_all() + if not optimize: + if linfo.validate_all(): + raise SyntaxError("Can't build lexer") + + if optimize and lextab: + try: + lexobj.readtab(lextab,ldict) + token = lexobj.token + input = lexobj.input + lexer = lexobj + return lexobj + + except ImportError: + pass + + # Dump some basic debugging information + if debug: + debuglog.info("lex: tokens = %r", linfo.tokens) + debuglog.info("lex: literals = %r", linfo.literals) + debuglog.info("lex: states = %r", linfo.stateinfo) + + # Build a dictionary of valid token names + lexobj.lextokens = { } + for n in linfo.tokens: + lexobj.lextokens[n] = 1 + + # Get literals specification + if isinstance(linfo.literals,(list,tuple)): + lexobj.lexliterals = type(linfo.literals[0])().join(linfo.literals) + else: + lexobj.lexliterals = linfo.literals + + # Get the stateinfo dictionary + stateinfo = linfo.stateinfo + + regexs = { } + # Build the master regular expressions + for state in stateinfo: + regex_list = [] + + # Add rules defined by functions first + for fname, f in linfo.funcsym[state]: + line = func_code(f).co_firstlineno + file = func_code(f).co_filename + regex_list.append("(?P<%s>%s)" % (fname,f.__doc__)) + if debug: + debuglog.info("lex: Adding rule %s -> '%s' (state '%s')",fname,f.__doc__, state) + + # Now add all of the simple rules + for name,r in linfo.strsym[state]: + regex_list.append("(?P<%s>%s)" % (name,r)) + if debug: + debuglog.info("lex: Adding rule %s -> '%s' (state '%s')",name,r, state) + + regexs[state] = regex_list + + # Build the master regular expressions + + if debug: + debuglog.info("lex: ==== MASTER REGEXS FOLLOW ====") + + for state in regexs: + lexre, re_text, re_names = _form_master_re(regexs[state],reflags,ldict,linfo.toknames) + lexobj.lexstatere[state] = lexre + lexobj.lexstateretext[state] = re_text + lexobj.lexstaterenames[state] = re_names + if debug: + for i in range(len(re_text)): + debuglog.info("lex: state '%s' : regex[%d] = '%s'",state, i, re_text[i]) + + # For inclusive states, we need to add the regular expressions from the INITIAL state + for state,stype in stateinfo.items(): + if state != "INITIAL" and stype == 'inclusive': + lexobj.lexstatere[state].extend(lexobj.lexstatere['INITIAL']) + lexobj.lexstateretext[state].extend(lexobj.lexstateretext['INITIAL']) + lexobj.lexstaterenames[state].extend(lexobj.lexstaterenames['INITIAL']) + + lexobj.lexstateinfo = stateinfo + lexobj.lexre = lexobj.lexstatere["INITIAL"] + lexobj.lexretext = lexobj.lexstateretext["INITIAL"] + lexobj.lexreflags = reflags + + # Set up ignore variables + lexobj.lexstateignore = linfo.ignore + lexobj.lexignore = lexobj.lexstateignore.get("INITIAL","") + + # Set up error functions + lexobj.lexstateerrorf = linfo.errorf + lexobj.lexerrorf = linfo.errorf.get("INITIAL",None) + if not lexobj.lexerrorf: + errorlog.warning("No t_error rule is defined") + + # Check state information for ignore and error rules + for s,stype in stateinfo.items(): + if stype == 'exclusive': + if not s in linfo.errorf: + errorlog.warning("No error rule is defined for exclusive state '%s'", s) + if not s in linfo.ignore and lexobj.lexignore: + errorlog.warning("No ignore rule is defined for exclusive state '%s'", s) + elif stype == 'inclusive': + if not s in linfo.errorf: + linfo.errorf[s] = linfo.errorf.get("INITIAL",None) + if not s in linfo.ignore: + linfo.ignore[s] = linfo.ignore.get("INITIAL","") + + # Create global versions of the token() and input() functions + token = lexobj.token + input = lexobj.input + lexer = lexobj + + # If in optimize mode, we write the lextab + if lextab and optimize: + lexobj.writetab(lextab,outputdir) + + return lexobj + +# ----------------------------------------------------------------------------- +# runmain() +# +# This runs the lexer as a main program +# ----------------------------------------------------------------------------- + +def runmain(lexer=None,data=None): + if not data: + try: + filename = sys.argv[1] + f = open(filename) + data = f.read() + f.close() + except IndexError: + sys.stdout.write("Reading from standard input (type EOF to end):\n") + data = sys.stdin.read() + + if lexer: + _input = lexer.input + else: + _input = input + _input(data) + if lexer: + _token = lexer.token + else: + _token = token + + while 1: + tok = _token() + if not tok: break + sys.stdout.write("(%s,%r,%d,%d)\n" % (tok.type, tok.value, tok.lineno,tok.lexpos)) + +# ----------------------------------------------------------------------------- +# @TOKEN(regex) +# +# This decorator function can be used to set the regex expression on a function +# when its docstring might need to be set in an alternative way +# ----------------------------------------------------------------------------- + +def TOKEN(r): + def set_doc(f): + if hasattr(r,"__call__"): + f.__doc__ = r.__doc__ + else: + f.__doc__ = r + return f + return set_doc + +# Alternative spelling of the TOKEN decorator +Token = TOKEN + diff --git a/mojo/public/third_party/ply/license.patch b/mojo/public/third_party/ply/license.patch new file mode 100644 index 0000000..7b2621f --- /dev/null +++ b/mojo/public/third_party/ply/license.patch @@ -0,0 +1,41 @@ +diff --git a/third_party/ply/__init__.py b/third_party/ply/__init__.py +index 853a985..f3da03e 100644 +--- a/third_party/ply/__init__.py ++++ b/third_party/ply/__init__.py +@@ -1,4 +1,36 @@ + # PLY package + # Author: David Beazley (dave@dabeaz.com) ++# ----------------------------------------------------------------------------- ++# ply: yacc.py ++# ++# Copyright (C) 2001-2011, ++# David M. Beazley (Dabeaz LLC) ++# All rights reserved. ++# ++# Redistribution and use in source and binary forms, with or without ++# modification, are permitted provided that the following conditions are ++# met: ++# ++# * Redistributions of source code must retain the above copyright notice, ++# this list of conditions and the following disclaimer. ++# * Redistributions in binary form must reproduce the above copyright notice, ++# this list of conditions and the following disclaimer in the documentation ++# and/or other materials provided with the distribution. ++# * Neither the name of the David Beazley or Dabeaz LLC may be used to ++# endorse or promote products derived from this software without ++# specific prior written permission. ++# ++# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++# ----------------------------------------------------------------------------- + + __all__ = ['lex','yacc'] diff --git a/mojo/public/third_party/ply/yacc.py b/mojo/public/third_party/ply/yacc.py new file mode 100644 index 0000000..f70439e --- /dev/null +++ b/mojo/public/third_party/ply/yacc.py @@ -0,0 +1,3276 @@ +# ----------------------------------------------------------------------------- +# ply: yacc.py +# +# Copyright (C) 2001-2011, +# David M. Beazley (Dabeaz LLC) +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of the David Beazley or Dabeaz LLC may be used to +# endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# ----------------------------------------------------------------------------- +# +# This implements an LR parser that is constructed from grammar rules defined +# as Python functions. The grammer is specified by supplying the BNF inside +# Python documentation strings. The inspiration for this technique was borrowed +# from John Aycock's Spark parsing system. PLY might be viewed as cross between +# Spark and the GNU bison utility. +# +# The current implementation is only somewhat object-oriented. The +# LR parser itself is defined in terms of an object (which allows multiple +# parsers to co-exist). However, most of the variables used during table +# construction are defined in terms of global variables. Users shouldn't +# notice unless they are trying to define multiple parsers at the same +# time using threads (in which case they should have their head examined). +# +# This implementation supports both SLR and LALR(1) parsing. LALR(1) +# support was originally implemented by Elias Ioup (ezioup@alumni.uchicago.edu), +# using the algorithm found in Aho, Sethi, and Ullman "Compilers: Principles, +# Techniques, and Tools" (The Dragon Book). LALR(1) has since been replaced +# by the more efficient DeRemer and Pennello algorithm. +# +# :::::::: WARNING ::::::: +# +# Construction of LR parsing tables is fairly complicated and expensive. +# To make this module run fast, a *LOT* of work has been put into +# optimization---often at the expensive of readability and what might +# consider to be good Python "coding style." Modify the code at your +# own risk! +# ---------------------------------------------------------------------------- + +__version__ = "3.4" +__tabversion__ = "3.2" # Table version + +#----------------------------------------------------------------------------- +# === User configurable parameters === +# +# Change these to modify the default behavior of yacc (if you wish) +#----------------------------------------------------------------------------- + +yaccdebug = 1 # Debugging mode. If set, yacc generates a + # a 'parser.out' file in the current directory + +debug_file = 'parser.out' # Default name of the debugging file +tab_module = 'parsetab' # Default name of the table module +default_lr = 'LALR' # Default LR table generation method + +error_count = 3 # Number of symbols that must be shifted to leave recovery mode + +yaccdevel = 0 # Set to True if developing yacc. This turns off optimized + # implementations of certain functions. + +resultlimit = 40 # Size limit of results when running in debug mode. + +pickle_protocol = 0 # Protocol to use when writing pickle files + +import re, types, sys, os.path + +# Compatibility function for python 2.6/3.0 +if sys.version_info[0] < 3: + def func_code(f): + return f.func_code +else: + def func_code(f): + return f.__code__ + +# Compatibility +try: + MAXINT = sys.maxint +except AttributeError: + MAXINT = sys.maxsize + +# Python 2.x/3.0 compatibility. +def load_ply_lex(): + if sys.version_info[0] < 3: + import lex + else: + import ply.lex as lex + return lex + +# This object is a stand-in for a logging object created by the +# logging module. PLY will use this by default to create things +# such as the parser.out file. If a user wants more detailed +# information, they can create their own logging object and pass +# it into PLY. + +class PlyLogger(object): + def __init__(self,f): + self.f = f + def debug(self,msg,*args,**kwargs): + self.f.write((msg % args) + "\n") + info = debug + + def warning(self,msg,*args,**kwargs): + self.f.write("WARNING: "+ (msg % args) + "\n") + + def error(self,msg,*args,**kwargs): + self.f.write("ERROR: " + (msg % args) + "\n") + + critical = debug + +# Null logger is used when no output is generated. Does nothing. +class NullLogger(object): + def __getattribute__(self,name): + return self + def __call__(self,*args,**kwargs): + return self + +# Exception raised for yacc-related errors +class YaccError(Exception): pass + +# Format the result message that the parser produces when running in debug mode. +def format_result(r): + repr_str = repr(r) + if '\n' in repr_str: repr_str = repr(repr_str) + if len(repr_str) > resultlimit: + repr_str = repr_str[:resultlimit]+" ..." + result = "<%s @ 0x%x> (%s)" % (type(r).__name__,id(r),repr_str) + return result + + +# Format stack entries when the parser is running in debug mode +def format_stack_entry(r): + repr_str = repr(r) + if '\n' in repr_str: repr_str = repr(repr_str) + if len(repr_str) < 16: + return repr_str + else: + return "<%s @ 0x%x>" % (type(r).__name__,id(r)) + +#----------------------------------------------------------------------------- +# === LR Parsing Engine === +# +# The following classes are used for the LR parser itself. These are not +# used during table construction and are independent of the actual LR +# table generation algorithm +#----------------------------------------------------------------------------- + +# This class is used to hold non-terminal grammar symbols during parsing. +# It normally has the following attributes set: +# .type = Grammar symbol type +# .value = Symbol value +# .lineno = Starting line number +# .endlineno = Ending line number (optional, set automatically) +# .lexpos = Starting lex position +# .endlexpos = Ending lex position (optional, set automatically) + +class YaccSymbol: + def __str__(self): return self.type + def __repr__(self): return str(self) + +# This class is a wrapper around the objects actually passed to each +# grammar rule. Index lookup and assignment actually assign the +# .value attribute of the underlying YaccSymbol object. +# The lineno() method returns the line number of a given +# item (or 0 if not defined). The linespan() method returns +# a tuple of (startline,endline) representing the range of lines +# for a symbol. The lexspan() method returns a tuple (lexpos,endlexpos) +# representing the range of positional information for a symbol. + +class YaccProduction: + def __init__(self,s,stack=None): + self.slice = s + self.stack = stack + self.lexer = None + self.parser= None + def __getitem__(self,n): + if n >= 0: return self.slice[n].value + else: return self.stack[n].value + + def __setitem__(self,n,v): + self.slice[n].value = v + + def __getslice__(self,i,j): + return [s.value for s in self.slice[i:j]] + + def __len__(self): + return len(self.slice) + + def lineno(self,n): + return getattr(self.slice[n],"lineno",0) + + def set_lineno(self,n,lineno): + self.slice[n].lineno = lineno + + def linespan(self,n): + startline = getattr(self.slice[n],"lineno",0) + endline = getattr(self.slice[n],"endlineno",startline) + return startline,endline + + def lexpos(self,n): + return getattr(self.slice[n],"lexpos",0) + + def lexspan(self,n): + startpos = getattr(self.slice[n],"lexpos",0) + endpos = getattr(self.slice[n],"endlexpos",startpos) + return startpos,endpos + + def error(self): + raise SyntaxError + + +# ----------------------------------------------------------------------------- +# == LRParser == +# +# The LR Parsing engine. +# ----------------------------------------------------------------------------- + +class LRParser: + def __init__(self,lrtab,errorf): + self.productions = lrtab.lr_productions + self.action = lrtab.lr_action + self.goto = lrtab.lr_goto + self.errorfunc = errorf + + def errok(self): + self.errorok = 1 + + def restart(self): + del self.statestack[:] + del self.symstack[:] + sym = YaccSymbol() + sym.type = '$end' + self.symstack.append(sym) + self.statestack.append(0) + + def parse(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None): + if debug or yaccdevel: + if isinstance(debug,int): + debug = PlyLogger(sys.stderr) + return self.parsedebug(input,lexer,debug,tracking,tokenfunc) + elif tracking: + return self.parseopt(input,lexer,debug,tracking,tokenfunc) + else: + return self.parseopt_notrack(input,lexer,debug,tracking,tokenfunc) + + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # parsedebug(). + # + # This is the debugging enabled version of parse(). All changes made to the + # parsing engine should be made here. For the non-debugging version, + # copy this code to a method parseopt() and delete all of the sections + # enclosed in: + # + # #--! DEBUG + # statements + # #--! DEBUG + # + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + def parsedebug(self,input=None,lexer=None,debug=None,tracking=0,tokenfunc=None): + lookahead = None # Current lookahead symbol + lookaheadstack = [ ] # Stack of lookahead symbols + actions = self.action # Local reference to action table (to avoid lookup on self.) + goto = self.goto # Local reference to goto table (to avoid lookup on self.) + prod = self.productions # Local reference to production list (to avoid lookup on self.) + pslice = YaccProduction(None) # Production object passed to grammar rules + errorcount = 0 # Used during error recovery + + # --! DEBUG + debug.info("PLY: PARSE DEBUG START") + # --! DEBUG + + # If no lexer was given, we will try to use the lex module + if not lexer: + lex = load_ply_lex() + lexer = lex.lexer + + # Set up the lexer and parser objects on pslice + pslice.lexer = lexer + pslice.parser = self + + # If input was supplied, pass to lexer + if input is not None: + lexer.input(input) + + if tokenfunc is None: + # Tokenize function + get_token = lexer.token + else: + get_token = tokenfunc + + # Set up the state and symbol stacks + + statestack = [ ] # Stack of parsing states + self.statestack = statestack + symstack = [ ] # Stack of grammar symbols + self.symstack = symstack + + pslice.stack = symstack # Put in the production + errtoken = None # Err token + + # The start state is assumed to be (0,$end) + + statestack.append(0) + sym = YaccSymbol() + sym.type = "$end" + symstack.append(sym) + state = 0 + while 1: + # Get the next symbol on the input. If a lookahead symbol + # is already set, we just use that. Otherwise, we'll pull + # the next token off of the lookaheadstack or from the lexer + + # --! DEBUG + debug.debug('') + debug.debug('State : %s', state) + # --! DEBUG + + if not lookahead: + if not lookaheadstack: + lookahead = get_token() # Get the next token + else: + lookahead = lookaheadstack.pop() + if not lookahead: + lookahead = YaccSymbol() + lookahead.type = "$end" + + # --! DEBUG + debug.debug('Stack : %s', + ("%s . %s" % (" ".join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip()) + # --! DEBUG + + # Check the action table + ltype = lookahead.type + t = actions[state].get(ltype) + + if t is not None: + if t > 0: + # shift a symbol on the stack + statestack.append(t) + state = t + + # --! DEBUG + debug.debug("Action : Shift and goto state %s", t) + # --! DEBUG + + symstack.append(lookahead) + lookahead = None + + # Decrease error count on successful shift + if errorcount: errorcount -=1 + continue + + if t < 0: + # reduce a symbol on the stack, emit a production + p = prod[-t] + pname = p.name + plen = p.len + + # Get production function + sym = YaccSymbol() + sym.type = pname # Production name + sym.value = None + + # --! DEBUG + if plen: + debug.info("Action : Reduce rule [%s] with %s and goto state %d", p.str, "["+",".join([format_stack_entry(_v.value) for _v in symstack[-plen:]])+"]",-t) + else: + debug.info("Action : Reduce rule [%s] with %s and goto state %d", p.str, [],-t) + + # --! DEBUG + + if plen: + targ = symstack[-plen-1:] + targ[0] = sym + + # --! TRACKING + if tracking: + t1 = targ[1] + sym.lineno = t1.lineno + sym.lexpos = t1.lexpos + t1 = targ[-1] + sym.endlineno = getattr(t1,"endlineno",t1.lineno) + sym.endlexpos = getattr(t1,"endlexpos",t1.lexpos) + + # --! TRACKING + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # below as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + del symstack[-plen:] + del statestack[-plen:] + p.callable(pslice) + # --! DEBUG + debug.info("Result : %s", format_result(pslice[0])) + # --! DEBUG + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) + symstack.pop() + statestack.pop() + state = statestack[-1] + sym.type = 'error' + lookahead = sym + errorcount = error_count + self.errorok = 0 + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + else: + + # --! TRACKING + if tracking: + sym.lineno = lexer.lineno + sym.lexpos = lexer.lexpos + # --! TRACKING + + targ = [ sym ] + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # above as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + p.callable(pslice) + # --! DEBUG + debug.info("Result : %s", format_result(pslice[0])) + # --! DEBUG + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) + symstack.pop() + statestack.pop() + state = statestack[-1] + sym.type = 'error' + lookahead = sym + errorcount = error_count + self.errorok = 0 + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + if t == 0: + n = symstack[-1] + result = getattr(n,"value",None) + # --! DEBUG + debug.info("Done : Returning %s", format_result(result)) + debug.info("PLY: PARSE DEBUG END") + # --! DEBUG + return result + + if t == None: + + # --! DEBUG + debug.error('Error : %s', + ("%s . %s" % (" ".join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip()) + # --! DEBUG + + # We have some kind of parsing error here. To handle + # this, we are going to push the current token onto + # the tokenstack and replace it with an 'error' token. + # If there are any synchronization rules, they may + # catch it. + # + # In addition to pushing the error token, we call call + # the user defined p_error() function if this is the + # first syntax error. This function is only called if + # errorcount == 0. + if errorcount == 0 or self.errorok: + errorcount = error_count + self.errorok = 0 + errtoken = lookahead + if errtoken.type == "$end": + errtoken = None # End of file! + if self.errorfunc: + global errok,token,restart + errok = self.errok # Set some special functions available in error recovery + token = get_token + restart = self.restart + if errtoken and not hasattr(errtoken,'lexer'): + errtoken.lexer = lexer + tok = self.errorfunc(errtoken) + del errok, token, restart # Delete special functions + + if self.errorok: + # User must have done some kind of panic + # mode recovery on their own. The + # returned token is the next lookahead + lookahead = tok + errtoken = None + continue + else: + if errtoken: + if hasattr(errtoken,"lineno"): lineno = lookahead.lineno + else: lineno = 0 + if lineno: + sys.stderr.write("yacc: Syntax error at line %d, token=%s\n" % (lineno, errtoken.type)) + else: + sys.stderr.write("yacc: Syntax error, token=%s" % errtoken.type) + else: + sys.stderr.write("yacc: Parse error in input. EOF\n") + return + + else: + errorcount = error_count + + # case 1: the statestack only has 1 entry on it. If we're in this state, the + # entire parse has been rolled back and we're completely hosed. The token is + # discarded and we just keep going. + + if len(statestack) <= 1 and lookahead.type != "$end": + lookahead = None + errtoken = None + state = 0 + # Nuke the pushback stack + del lookaheadstack[:] + continue + + # case 2: the statestack has a couple of entries on it, but we're + # at the end of the file. nuke the top entry and generate an error token + + # Start nuking entries on the stack + if lookahead.type == "$end": + # Whoa. We're really hosed here. Bail out + return + + if lookahead.type != 'error': + sym = symstack[-1] + if sym.type == 'error': + # Hmmm. Error is on top of stack, we'll just nuke input + # symbol and continue + lookahead = None + continue + t = YaccSymbol() + t.type = 'error' + if hasattr(lookahead,"lineno"): + t.lineno = lookahead.lineno + t.value = lookahead + lookaheadstack.append(lookahead) + lookahead = t + else: + symstack.pop() + statestack.pop() + state = statestack[-1] # Potential bug fix + + continue + + # Call an error function here + raise RuntimeError("yacc: internal parser error!!!\n") + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # parseopt(). + # + # Optimized version of parse() method. DO NOT EDIT THIS CODE DIRECTLY. + # Edit the debug version above, then copy any modifications to the method + # below while removing #--! DEBUG sections. + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + + def parseopt(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None): + lookahead = None # Current lookahead symbol + lookaheadstack = [ ] # Stack of lookahead symbols + actions = self.action # Local reference to action table (to avoid lookup on self.) + goto = self.goto # Local reference to goto table (to avoid lookup on self.) + prod = self.productions # Local reference to production list (to avoid lookup on self.) + pslice = YaccProduction(None) # Production object passed to grammar rules + errorcount = 0 # Used during error recovery + + # If no lexer was given, we will try to use the lex module + if not lexer: + lex = load_ply_lex() + lexer = lex.lexer + + # Set up the lexer and parser objects on pslice + pslice.lexer = lexer + pslice.parser = self + + # If input was supplied, pass to lexer + if input is not None: + lexer.input(input) + + if tokenfunc is None: + # Tokenize function + get_token = lexer.token + else: + get_token = tokenfunc + + # Set up the state and symbol stacks + + statestack = [ ] # Stack of parsing states + self.statestack = statestack + symstack = [ ] # Stack of grammar symbols + self.symstack = symstack + + pslice.stack = symstack # Put in the production + errtoken = None # Err token + + # The start state is assumed to be (0,$end) + + statestack.append(0) + sym = YaccSymbol() + sym.type = '$end' + symstack.append(sym) + state = 0 + while 1: + # Get the next symbol on the input. If a lookahead symbol + # is already set, we just use that. Otherwise, we'll pull + # the next token off of the lookaheadstack or from the lexer + + if not lookahead: + if not lookaheadstack: + lookahead = get_token() # Get the next token + else: + lookahead = lookaheadstack.pop() + if not lookahead: + lookahead = YaccSymbol() + lookahead.type = '$end' + + # Check the action table + ltype = lookahead.type + t = actions[state].get(ltype) + + if t is not None: + if t > 0: + # shift a symbol on the stack + statestack.append(t) + state = t + + symstack.append(lookahead) + lookahead = None + + # Decrease error count on successful shift + if errorcount: errorcount -=1 + continue + + if t < 0: + # reduce a symbol on the stack, emit a production + p = prod[-t] + pname = p.name + plen = p.len + + # Get production function + sym = YaccSymbol() + sym.type = pname # Production name + sym.value = None + + if plen: + targ = symstack[-plen-1:] + targ[0] = sym + + # --! TRACKING + if tracking: + t1 = targ[1] + sym.lineno = t1.lineno + sym.lexpos = t1.lexpos + t1 = targ[-1] + sym.endlineno = getattr(t1,"endlineno",t1.lineno) + sym.endlexpos = getattr(t1,"endlexpos",t1.lexpos) + + # --! TRACKING + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # below as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + del symstack[-plen:] + del statestack[-plen:] + p.callable(pslice) + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) + symstack.pop() + statestack.pop() + state = statestack[-1] + sym.type = 'error' + lookahead = sym + errorcount = error_count + self.errorok = 0 + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + else: + + # --! TRACKING + if tracking: + sym.lineno = lexer.lineno + sym.lexpos = lexer.lexpos + # --! TRACKING + + targ = [ sym ] + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # above as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + p.callable(pslice) + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) + symstack.pop() + statestack.pop() + state = statestack[-1] + sym.type = 'error' + lookahead = sym + errorcount = error_count + self.errorok = 0 + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + if t == 0: + n = symstack[-1] + return getattr(n,"value",None) + + if t == None: + + # We have some kind of parsing error here. To handle + # this, we are going to push the current token onto + # the tokenstack and replace it with an 'error' token. + # If there are any synchronization rules, they may + # catch it. + # + # In addition to pushing the error token, we call call + # the user defined p_error() function if this is the + # first syntax error. This function is only called if + # errorcount == 0. + if errorcount == 0 or self.errorok: + errorcount = error_count + self.errorok = 0 + errtoken = lookahead + if errtoken.type == '$end': + errtoken = None # End of file! + if self.errorfunc: + global errok,token,restart + errok = self.errok # Set some special functions available in error recovery + token = get_token + restart = self.restart + if errtoken and not hasattr(errtoken,'lexer'): + errtoken.lexer = lexer + tok = self.errorfunc(errtoken) + del errok, token, restart # Delete special functions + + if self.errorok: + # User must have done some kind of panic + # mode recovery on their own. The + # returned token is the next lookahead + lookahead = tok + errtoken = None + continue + else: + if errtoken: + if hasattr(errtoken,"lineno"): lineno = lookahead.lineno + else: lineno = 0 + if lineno: + sys.stderr.write("yacc: Syntax error at line %d, token=%s\n" % (lineno, errtoken.type)) + else: + sys.stderr.write("yacc: Syntax error, token=%s" % errtoken.type) + else: + sys.stderr.write("yacc: Parse error in input. EOF\n") + return + + else: + errorcount = error_count + + # case 1: the statestack only has 1 entry on it. If we're in this state, the + # entire parse has been rolled back and we're completely hosed. The token is + # discarded and we just keep going. + + if len(statestack) <= 1 and lookahead.type != '$end': + lookahead = None + errtoken = None + state = 0 + # Nuke the pushback stack + del lookaheadstack[:] + continue + + # case 2: the statestack has a couple of entries on it, but we're + # at the end of the file. nuke the top entry and generate an error token + + # Start nuking entries on the stack + if lookahead.type == '$end': + # Whoa. We're really hosed here. Bail out + return + + if lookahead.type != 'error': + sym = symstack[-1] + if sym.type == 'error': + # Hmmm. Error is on top of stack, we'll just nuke input + # symbol and continue + lookahead = None + continue + t = YaccSymbol() + t.type = 'error' + if hasattr(lookahead,"lineno"): + t.lineno = lookahead.lineno + t.value = lookahead + lookaheadstack.append(lookahead) + lookahead = t + else: + symstack.pop() + statestack.pop() + state = statestack[-1] # Potential bug fix + + continue + + # Call an error function here + raise RuntimeError("yacc: internal parser error!!!\n") + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # parseopt_notrack(). + # + # Optimized version of parseopt() with line number tracking removed. + # DO NOT EDIT THIS CODE DIRECTLY. Copy the optimized version and remove + # code in the #--! TRACKING sections + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + def parseopt_notrack(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None): + lookahead = None # Current lookahead symbol + lookaheadstack = [ ] # Stack of lookahead symbols + actions = self.action # Local reference to action table (to avoid lookup on self.) + goto = self.goto # Local reference to goto table (to avoid lookup on self.) + prod = self.productions # Local reference to production list (to avoid lookup on self.) + pslice = YaccProduction(None) # Production object passed to grammar rules + errorcount = 0 # Used during error recovery + + # If no lexer was given, we will try to use the lex module + if not lexer: + lex = load_ply_lex() + lexer = lex.lexer + + # Set up the lexer and parser objects on pslice + pslice.lexer = lexer + pslice.parser = self + + # If input was supplied, pass to lexer + if input is not None: + lexer.input(input) + + if tokenfunc is None: + # Tokenize function + get_token = lexer.token + else: + get_token = tokenfunc + + # Set up the state and symbol stacks + + statestack = [ ] # Stack of parsing states + self.statestack = statestack + symstack = [ ] # Stack of grammar symbols + self.symstack = symstack + + pslice.stack = symstack # Put in the production + errtoken = None # Err token + + # The start state is assumed to be (0,$end) + + statestack.append(0) + sym = YaccSymbol() + sym.type = '$end' + symstack.append(sym) + state = 0 + while 1: + # Get the next symbol on the input. If a lookahead symbol + # is already set, we just use that. Otherwise, we'll pull + # the next token off of the lookaheadstack or from the lexer + + if not lookahead: + if not lookaheadstack: + lookahead = get_token() # Get the next token + else: + lookahead = lookaheadstack.pop() + if not lookahead: + lookahead = YaccSymbol() + lookahead.type = '$end' + + # Check the action table + ltype = lookahead.type + t = actions[state].get(ltype) + + if t is not None: + if t > 0: + # shift a symbol on the stack + statestack.append(t) + state = t + + symstack.append(lookahead) + lookahead = None + + # Decrease error count on successful shift + if errorcount: errorcount -=1 + continue + + if t < 0: + # reduce a symbol on the stack, emit a production + p = prod[-t] + pname = p.name + plen = p.len + + # Get production function + sym = YaccSymbol() + sym.type = pname # Production name + sym.value = None + + if plen: + targ = symstack[-plen-1:] + targ[0] = sym + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # below as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + del symstack[-plen:] + del statestack[-plen:] + p.callable(pslice) + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) + symstack.pop() + statestack.pop() + state = statestack[-1] + sym.type = 'error' + lookahead = sym + errorcount = error_count + self.errorok = 0 + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + else: + + targ = [ sym ] + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # above as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + p.callable(pslice) + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) + symstack.pop() + statestack.pop() + state = statestack[-1] + sym.type = 'error' + lookahead = sym + errorcount = error_count + self.errorok = 0 + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + if t == 0: + n = symstack[-1] + return getattr(n,"value",None) + + if t == None: + + # We have some kind of parsing error here. To handle + # this, we are going to push the current token onto + # the tokenstack and replace it with an 'error' token. + # If there are any synchronization rules, they may + # catch it. + # + # In addition to pushing the error token, we call call + # the user defined p_error() function if this is the + # first syntax error. This function is only called if + # errorcount == 0. + if errorcount == 0 or self.errorok: + errorcount = error_count + self.errorok = 0 + errtoken = lookahead + if errtoken.type == '$end': + errtoken = None # End of file! + if self.errorfunc: + global errok,token,restart + errok = self.errok # Set some special functions available in error recovery + token = get_token + restart = self.restart + if errtoken and not hasattr(errtoken,'lexer'): + errtoken.lexer = lexer + tok = self.errorfunc(errtoken) + del errok, token, restart # Delete special functions + + if self.errorok: + # User must have done some kind of panic + # mode recovery on their own. The + # returned token is the next lookahead + lookahead = tok + errtoken = None + continue + else: + if errtoken: + if hasattr(errtoken,"lineno"): lineno = lookahead.lineno + else: lineno = 0 + if lineno: + sys.stderr.write("yacc: Syntax error at line %d, token=%s\n" % (lineno, errtoken.type)) + else: + sys.stderr.write("yacc: Syntax error, token=%s" % errtoken.type) + else: + sys.stderr.write("yacc: Parse error in input. EOF\n") + return + + else: + errorcount = error_count + + # case 1: the statestack only has 1 entry on it. If we're in this state, the + # entire parse has been rolled back and we're completely hosed. The token is + # discarded and we just keep going. + + if len(statestack) <= 1 and lookahead.type != '$end': + lookahead = None + errtoken = None + state = 0 + # Nuke the pushback stack + del lookaheadstack[:] + continue + + # case 2: the statestack has a couple of entries on it, but we're + # at the end of the file. nuke the top entry and generate an error token + + # Start nuking entries on the stack + if lookahead.type == '$end': + # Whoa. We're really hosed here. Bail out + return + + if lookahead.type != 'error': + sym = symstack[-1] + if sym.type == 'error': + # Hmmm. Error is on top of stack, we'll just nuke input + # symbol and continue + lookahead = None + continue + t = YaccSymbol() + t.type = 'error' + if hasattr(lookahead,"lineno"): + t.lineno = lookahead.lineno + t.value = lookahead + lookaheadstack.append(lookahead) + lookahead = t + else: + symstack.pop() + statestack.pop() + state = statestack[-1] # Potential bug fix + + continue + + # Call an error function here + raise RuntimeError("yacc: internal parser error!!!\n") + +# ----------------------------------------------------------------------------- +# === Grammar Representation === +# +# The following functions, classes, and variables are used to represent and +# manipulate the rules that make up a grammar. +# ----------------------------------------------------------------------------- + +import re + +# regex matching identifiers +_is_identifier = re.compile(r'^[a-zA-Z0-9_-]+$') + +# ----------------------------------------------------------------------------- +# class Production: +# +# This class stores the raw information about a single production or grammar rule. +# A grammar rule refers to a specification such as this: +# +# expr : expr PLUS term +# +# Here are the basic attributes defined on all productions +# +# name - Name of the production. For example 'expr' +# prod - A list of symbols on the right side ['expr','PLUS','term'] +# prec - Production precedence level +# number - Production number. +# func - Function that executes on reduce +# file - File where production function is defined +# lineno - Line number where production function is defined +# +# The following attributes are defined or optional. +# +# len - Length of the production (number of symbols on right hand side) +# usyms - Set of unique symbols found in the production +# ----------------------------------------------------------------------------- + +class Production(object): + reduced = 0 + def __init__(self,number,name,prod,precedence=('right',0),func=None,file='',line=0): + self.name = name + self.prod = tuple(prod) + self.number = number + self.func = func + self.callable = None + self.file = file + self.line = line + self.prec = precedence + + # Internal settings used during table construction + + self.len = len(self.prod) # Length of the production + + # Create a list of unique production symbols used in the production + self.usyms = [ ] + for s in self.prod: + if s not in self.usyms: + self.usyms.append(s) + + # List of all LR items for the production + self.lr_items = [] + self.lr_next = None + + # Create a string representation + if self.prod: + self.str = "%s -> %s" % (self.name," ".join(self.prod)) + else: + self.str = "%s -> <empty>" % self.name + + def __str__(self): + return self.str + + def __repr__(self): + return "Production("+str(self)+")" + + def __len__(self): + return len(self.prod) + + def __nonzero__(self): + return 1 + + def __getitem__(self,index): + return self.prod[index] + + # Return the nth lr_item from the production (or None if at the end) + def lr_item(self,n): + if n > len(self.prod): return None + p = LRItem(self,n) + + # Precompute the list of productions immediately following. Hack. Remove later + try: + p.lr_after = Prodnames[p.prod[n+1]] + except (IndexError,KeyError): + p.lr_after = [] + try: + p.lr_before = p.prod[n-1] + except IndexError: + p.lr_before = None + + return p + + # Bind the production function name to a callable + def bind(self,pdict): + if self.func: + self.callable = pdict[self.func] + +# This class serves as a minimal standin for Production objects when +# reading table data from files. It only contains information +# actually used by the LR parsing engine, plus some additional +# debugging information. +class MiniProduction(object): + def __init__(self,str,name,len,func,file,line): + self.name = name + self.len = len + self.func = func + self.callable = None + self.file = file + self.line = line + self.str = str + def __str__(self): + return self.str + def __repr__(self): + return "MiniProduction(%s)" % self.str + + # Bind the production function name to a callable + def bind(self,pdict): + if self.func: + self.callable = pdict[self.func] + + +# ----------------------------------------------------------------------------- +# class LRItem +# +# This class represents a specific stage of parsing a production rule. For +# example: +# +# expr : expr . PLUS term +# +# In the above, the "." represents the current location of the parse. Here +# basic attributes: +# +# name - Name of the production. For example 'expr' +# prod - A list of symbols on the right side ['expr','.', 'PLUS','term'] +# number - Production number. +# +# lr_next Next LR item. Example, if we are ' expr -> expr . PLUS term' +# then lr_next refers to 'expr -> expr PLUS . term' +# lr_index - LR item index (location of the ".") in the prod list. +# lookaheads - LALR lookahead symbols for this item +# len - Length of the production (number of symbols on right hand side) +# lr_after - List of all productions that immediately follow +# lr_before - Grammar symbol immediately before +# ----------------------------------------------------------------------------- + +class LRItem(object): + def __init__(self,p,n): + self.name = p.name + self.prod = list(p.prod) + self.number = p.number + self.lr_index = n + self.lookaheads = { } + self.prod.insert(n,".") + self.prod = tuple(self.prod) + self.len = len(self.prod) + self.usyms = p.usyms + + def __str__(self): + if self.prod: + s = "%s -> %s" % (self.name," ".join(self.prod)) + else: + s = "%s -> <empty>" % self.name + return s + + def __repr__(self): + return "LRItem("+str(self)+")" + +# ----------------------------------------------------------------------------- +# rightmost_terminal() +# +# Return the rightmost terminal from a list of symbols. Used in add_production() +# ----------------------------------------------------------------------------- +def rightmost_terminal(symbols, terminals): + i = len(symbols) - 1 + while i >= 0: + if symbols[i] in terminals: + return symbols[i] + i -= 1 + return None + +# ----------------------------------------------------------------------------- +# === GRAMMAR CLASS === +# +# The following class represents the contents of the specified grammar along +# with various computed properties such as first sets, follow sets, LR items, etc. +# This data is used for critical parts of the table generation process later. +# ----------------------------------------------------------------------------- + +class GrammarError(YaccError): pass + +class Grammar(object): + def __init__(self,terminals): + self.Productions = [None] # A list of all of the productions. The first + # entry is always reserved for the purpose of + # building an augmented grammar + + self.Prodnames = { } # A dictionary mapping the names of nonterminals to a list of all + # productions of that nonterminal. + + self.Prodmap = { } # A dictionary that is only used to detect duplicate + # productions. + + self.Terminals = { } # A dictionary mapping the names of terminal symbols to a + # list of the rules where they are used. + + for term in terminals: + self.Terminals[term] = [] + + self.Terminals['error'] = [] + + self.Nonterminals = { } # A dictionary mapping names of nonterminals to a list + # of rule numbers where they are used. + + self.First = { } # A dictionary of precomputed FIRST(x) symbols + + self.Follow = { } # A dictionary of precomputed FOLLOW(x) symbols + + self.Precedence = { } # Precedence rules for each terminal. Contains tuples of the + # form ('right',level) or ('nonassoc', level) or ('left',level) + + self.UsedPrecedence = { } # Precedence rules that were actually used by the grammer. + # This is only used to provide error checking and to generate + # a warning about unused precedence rules. + + self.Start = None # Starting symbol for the grammar + + + def __len__(self): + return len(self.Productions) + + def __getitem__(self,index): + return self.Productions[index] + + # ----------------------------------------------------------------------------- + # set_precedence() + # + # Sets the precedence for a given terminal. assoc is the associativity such as + # 'left','right', or 'nonassoc'. level is a numeric level. + # + # ----------------------------------------------------------------------------- + + def set_precedence(self,term,assoc,level): + assert self.Productions == [None],"Must call set_precedence() before add_production()" + if term in self.Precedence: + raise GrammarError("Precedence already specified for terminal '%s'" % term) + if assoc not in ['left','right','nonassoc']: + raise GrammarError("Associativity must be one of 'left','right', or 'nonassoc'") + self.Precedence[term] = (assoc,level) + + # ----------------------------------------------------------------------------- + # add_production() + # + # Given an action function, this function assembles a production rule and + # computes its precedence level. + # + # The production rule is supplied as a list of symbols. For example, + # a rule such as 'expr : expr PLUS term' has a production name of 'expr' and + # symbols ['expr','PLUS','term']. + # + # Precedence is determined by the precedence of the right-most non-terminal + # or the precedence of a terminal specified by %prec. + # + # A variety of error checks are performed to make sure production symbols + # are valid and that %prec is used correctly. + # ----------------------------------------------------------------------------- + + def add_production(self,prodname,syms,func=None,file='',line=0): + + if prodname in self.Terminals: + raise GrammarError("%s:%d: Illegal rule name '%s'. Already defined as a token" % (file,line,prodname)) + if prodname == 'error': + raise GrammarError("%s:%d: Illegal rule name '%s'. error is a reserved word" % (file,line,prodname)) + if not _is_identifier.match(prodname): + raise GrammarError("%s:%d: Illegal rule name '%s'" % (file,line,prodname)) + + # Look for literal tokens + for n,s in enumerate(syms): + if s[0] in "'\"": + try: + c = eval(s) + if (len(c) > 1): + raise GrammarError("%s:%d: Literal token %s in rule '%s' may only be a single character" % (file,line,s, prodname)) + if not c in self.Terminals: + self.Terminals[c] = [] + syms[n] = c + continue + except SyntaxError: + pass + if not _is_identifier.match(s) and s != '%prec': + raise GrammarError("%s:%d: Illegal name '%s' in rule '%s'" % (file,line,s, prodname)) + + # Determine the precedence level + if '%prec' in syms: + if syms[-1] == '%prec': + raise GrammarError("%s:%d: Syntax error. Nothing follows %%prec" % (file,line)) + if syms[-2] != '%prec': + raise GrammarError("%s:%d: Syntax error. %%prec can only appear at the end of a grammar rule" % (file,line)) + precname = syms[-1] + prodprec = self.Precedence.get(precname,None) + if not prodprec: + raise GrammarError("%s:%d: Nothing known about the precedence of '%s'" % (file,line,precname)) + else: + self.UsedPrecedence[precname] = 1 + del syms[-2:] # Drop %prec from the rule + else: + # If no %prec, precedence is determined by the rightmost terminal symbol + precname = rightmost_terminal(syms,self.Terminals) + prodprec = self.Precedence.get(precname,('right',0)) + + # See if the rule is already in the rulemap + map = "%s -> %s" % (prodname,syms) + if map in self.Prodmap: + m = self.Prodmap[map] + raise GrammarError("%s:%d: Duplicate rule %s. " % (file,line, m) + + "Previous definition at %s:%d" % (m.file, m.line)) + + # From this point on, everything is valid. Create a new Production instance + pnumber = len(self.Productions) + if not prodname in self.Nonterminals: + self.Nonterminals[prodname] = [ ] + + # Add the production number to Terminals and Nonterminals + for t in syms: + if t in self.Terminals: + self.Terminals[t].append(pnumber) + else: + if not t in self.Nonterminals: + self.Nonterminals[t] = [ ] + self.Nonterminals[t].append(pnumber) + + # Create a production and add it to the list of productions + p = Production(pnumber,prodname,syms,prodprec,func,file,line) + self.Productions.append(p) + self.Prodmap[map] = p + + # Add to the global productions list + try: + self.Prodnames[prodname].append(p) + except KeyError: + self.Prodnames[prodname] = [ p ] + return 0 + + # ----------------------------------------------------------------------------- + # set_start() + # + # Sets the starting symbol and creates the augmented grammar. Production + # rule 0 is S' -> start where start is the start symbol. + # ----------------------------------------------------------------------------- + + def set_start(self,start=None): + if not start: + start = self.Productions[1].name + if start not in self.Nonterminals: + raise GrammarError("start symbol %s undefined" % start) + self.Productions[0] = Production(0,"S'",[start]) + self.Nonterminals[start].append(0) + self.Start = start + + # ----------------------------------------------------------------------------- + # find_unreachable() + # + # Find all of the nonterminal symbols that can't be reached from the starting + # symbol. Returns a list of nonterminals that can't be reached. + # ----------------------------------------------------------------------------- + + def find_unreachable(self): + + # Mark all symbols that are reachable from a symbol s + def mark_reachable_from(s): + if reachable[s]: + # We've already reached symbol s. + return + reachable[s] = 1 + for p in self.Prodnames.get(s,[]): + for r in p.prod: + mark_reachable_from(r) + + reachable = { } + for s in list(self.Terminals) + list(self.Nonterminals): + reachable[s] = 0 + + mark_reachable_from( self.Productions[0].prod[0] ) + + return [s for s in list(self.Nonterminals) + if not reachable[s]] + + # ----------------------------------------------------------------------------- + # infinite_cycles() + # + # This function looks at the various parsing rules and tries to detect + # infinite recursion cycles (grammar rules where there is no possible way + # to derive a string of only terminals). + # ----------------------------------------------------------------------------- + + def infinite_cycles(self): + terminates = {} + + # Terminals: + for t in self.Terminals: + terminates[t] = 1 + + terminates['$end'] = 1 + + # Nonterminals: + + # Initialize to false: + for n in self.Nonterminals: + terminates[n] = 0 + + # Then propagate termination until no change: + while 1: + some_change = 0 + for (n,pl) in self.Prodnames.items(): + # Nonterminal n terminates iff any of its productions terminates. + for p in pl: + # Production p terminates iff all of its rhs symbols terminate. + for s in p.prod: + if not terminates[s]: + # The symbol s does not terminate, + # so production p does not terminate. + p_terminates = 0 + break + else: + # didn't break from the loop, + # so every symbol s terminates + # so production p terminates. + p_terminates = 1 + + if p_terminates: + # symbol n terminates! + if not terminates[n]: + terminates[n] = 1 + some_change = 1 + # Don't need to consider any more productions for this n. + break + + if not some_change: + break + + infinite = [] + for (s,term) in terminates.items(): + if not term: + if not s in self.Prodnames and not s in self.Terminals and s != 'error': + # s is used-but-not-defined, and we've already warned of that, + # so it would be overkill to say that it's also non-terminating. + pass + else: + infinite.append(s) + + return infinite + + + # ----------------------------------------------------------------------------- + # undefined_symbols() + # + # Find all symbols that were used the grammar, but not defined as tokens or + # grammar rules. Returns a list of tuples (sym, prod) where sym in the symbol + # and prod is the production where the symbol was used. + # ----------------------------------------------------------------------------- + def undefined_symbols(self): + result = [] + for p in self.Productions: + if not p: continue + + for s in p.prod: + if not s in self.Prodnames and not s in self.Terminals and s != 'error': + result.append((s,p)) + return result + + # ----------------------------------------------------------------------------- + # unused_terminals() + # + # Find all terminals that were defined, but not used by the grammar. Returns + # a list of all symbols. + # ----------------------------------------------------------------------------- + def unused_terminals(self): + unused_tok = [] + for s,v in self.Terminals.items(): + if s != 'error' and not v: + unused_tok.append(s) + + return unused_tok + + # ------------------------------------------------------------------------------ + # unused_rules() + # + # Find all grammar rules that were defined, but not used (maybe not reachable) + # Returns a list of productions. + # ------------------------------------------------------------------------------ + + def unused_rules(self): + unused_prod = [] + for s,v in self.Nonterminals.items(): + if not v: + p = self.Prodnames[s][0] + unused_prod.append(p) + return unused_prod + + # ----------------------------------------------------------------------------- + # unused_precedence() + # + # Returns a list of tuples (term,precedence) corresponding to precedence + # rules that were never used by the grammar. term is the name of the terminal + # on which precedence was applied and precedence is a string such as 'left' or + # 'right' corresponding to the type of precedence. + # ----------------------------------------------------------------------------- + + def unused_precedence(self): + unused = [] + for termname in self.Precedence: + if not (termname in self.Terminals or termname in self.UsedPrecedence): + unused.append((termname,self.Precedence[termname][0])) + + return unused + + # ------------------------------------------------------------------------- + # _first() + # + # Compute the value of FIRST1(beta) where beta is a tuple of symbols. + # + # During execution of compute_first1, the result may be incomplete. + # Afterward (e.g., when called from compute_follow()), it will be complete. + # ------------------------------------------------------------------------- + def _first(self,beta): + + # We are computing First(x1,x2,x3,...,xn) + result = [ ] + for x in beta: + x_produces_empty = 0 + + # Add all the non-<empty> symbols of First[x] to the result. + for f in self.First[x]: + if f == '<empty>': + x_produces_empty = 1 + else: + if f not in result: result.append(f) + + if x_produces_empty: + # We have to consider the next x in beta, + # i.e. stay in the loop. + pass + else: + # We don't have to consider any further symbols in beta. + break + else: + # There was no 'break' from the loop, + # so x_produces_empty was true for all x in beta, + # so beta produces empty as well. + result.append('<empty>') + + return result + + # ------------------------------------------------------------------------- + # compute_first() + # + # Compute the value of FIRST1(X) for all symbols + # ------------------------------------------------------------------------- + def compute_first(self): + if self.First: + return self.First + + # Terminals: + for t in self.Terminals: + self.First[t] = [t] + + self.First['$end'] = ['$end'] + + # Nonterminals: + + # Initialize to the empty set: + for n in self.Nonterminals: + self.First[n] = [] + + # Then propagate symbols until no change: + while 1: + some_change = 0 + for n in self.Nonterminals: + for p in self.Prodnames[n]: + for f in self._first(p.prod): + if f not in self.First[n]: + self.First[n].append( f ) + some_change = 1 + if not some_change: + break + + return self.First + + # --------------------------------------------------------------------- + # compute_follow() + # + # Computes all of the follow sets for every non-terminal symbol. The + # follow set is the set of all symbols that might follow a given + # non-terminal. See the Dragon book, 2nd Ed. p. 189. + # --------------------------------------------------------------------- + def compute_follow(self,start=None): + # If already computed, return the result + if self.Follow: + return self.Follow + + # If first sets not computed yet, do that first. + if not self.First: + self.compute_first() + + # Add '$end' to the follow list of the start symbol + for k in self.Nonterminals: + self.Follow[k] = [ ] + + if not start: + start = self.Productions[1].name + + self.Follow[start] = [ '$end' ] + + while 1: + didadd = 0 + for p in self.Productions[1:]: + # Here is the production set + for i in range(len(p.prod)): + B = p.prod[i] + if B in self.Nonterminals: + # Okay. We got a non-terminal in a production + fst = self._first(p.prod[i+1:]) + hasempty = 0 + for f in fst: + if f != '<empty>' and f not in self.Follow[B]: + self.Follow[B].append(f) + didadd = 1 + if f == '<empty>': + hasempty = 1 + if hasempty or i == (len(p.prod)-1): + # Add elements of follow(a) to follow(b) + for f in self.Follow[p.name]: + if f not in self.Follow[B]: + self.Follow[B].append(f) + didadd = 1 + if not didadd: break + return self.Follow + + + # ----------------------------------------------------------------------------- + # build_lritems() + # + # This function walks the list of productions and builds a complete set of the + # LR items. The LR items are stored in two ways: First, they are uniquely + # numbered and placed in the list _lritems. Second, a linked list of LR items + # is built for each production. For example: + # + # E -> E PLUS E + # + # Creates the list + # + # [E -> . E PLUS E, E -> E . PLUS E, E -> E PLUS . E, E -> E PLUS E . ] + # ----------------------------------------------------------------------------- + + def build_lritems(self): + for p in self.Productions: + lastlri = p + i = 0 + lr_items = [] + while 1: + if i > len(p): + lri = None + else: + lri = LRItem(p,i) + # Precompute the list of productions immediately following + try: + lri.lr_after = self.Prodnames[lri.prod[i+1]] + except (IndexError,KeyError): + lri.lr_after = [] + try: + lri.lr_before = lri.prod[i-1] + except IndexError: + lri.lr_before = None + + lastlri.lr_next = lri + if not lri: break + lr_items.append(lri) + lastlri = lri + i += 1 + p.lr_items = lr_items + +# ----------------------------------------------------------------------------- +# == Class LRTable == +# +# This basic class represents a basic table of LR parsing information. +# Methods for generating the tables are not defined here. They are defined +# in the derived class LRGeneratedTable. +# ----------------------------------------------------------------------------- + +class VersionError(YaccError): pass + +class LRTable(object): + def __init__(self): + self.lr_action = None + self.lr_goto = None + self.lr_productions = None + self.lr_method = None + + def read_table(self,module): + if isinstance(module,types.ModuleType): + parsetab = module + else: + if sys.version_info[0] < 3: + exec("import %s as parsetab" % module) + else: + env = { } + exec("import %s as parsetab" % module, env, env) + parsetab = env['parsetab'] + + if parsetab._tabversion != __tabversion__: + raise VersionError("yacc table file version is out of date") + + self.lr_action = parsetab._lr_action + self.lr_goto = parsetab._lr_goto + + self.lr_productions = [] + for p in parsetab._lr_productions: + self.lr_productions.append(MiniProduction(*p)) + + self.lr_method = parsetab._lr_method + return parsetab._lr_signature + + def read_pickle(self,filename): + try: + import cPickle as pickle + except ImportError: + import pickle + + in_f = open(filename,"rb") + + tabversion = pickle.load(in_f) + if tabversion != __tabversion__: + raise VersionError("yacc table file version is out of date") + self.lr_method = pickle.load(in_f) + signature = pickle.load(in_f) + self.lr_action = pickle.load(in_f) + self.lr_goto = pickle.load(in_f) + productions = pickle.load(in_f) + + self.lr_productions = [] + for p in productions: + self.lr_productions.append(MiniProduction(*p)) + + in_f.close() + return signature + + # Bind all production function names to callable objects in pdict + def bind_callables(self,pdict): + for p in self.lr_productions: + p.bind(pdict) + +# ----------------------------------------------------------------------------- +# === LR Generator === +# +# The following classes and functions are used to generate LR parsing tables on +# a grammar. +# ----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- +# digraph() +# traverse() +# +# The following two functions are used to compute set valued functions +# of the form: +# +# F(x) = F'(x) U U{F(y) | x R y} +# +# This is used to compute the values of Read() sets as well as FOLLOW sets +# in LALR(1) generation. +# +# Inputs: X - An input set +# R - A relation +# FP - Set-valued function +# ------------------------------------------------------------------------------ + +def digraph(X,R,FP): + N = { } + for x in X: + N[x] = 0 + stack = [] + F = { } + for x in X: + if N[x] == 0: traverse(x,N,stack,F,X,R,FP) + return F + +def traverse(x,N,stack,F,X,R,FP): + stack.append(x) + d = len(stack) + N[x] = d + F[x] = FP(x) # F(X) <- F'(x) + + rel = R(x) # Get y's related to x + for y in rel: + if N[y] == 0: + traverse(y,N,stack,F,X,R,FP) + N[x] = min(N[x],N[y]) + for a in F.get(y,[]): + if a not in F[x]: F[x].append(a) + if N[x] == d: + N[stack[-1]] = MAXINT + F[stack[-1]] = F[x] + element = stack.pop() + while element != x: + N[stack[-1]] = MAXINT + F[stack[-1]] = F[x] + element = stack.pop() + +class LALRError(YaccError): pass + +# ----------------------------------------------------------------------------- +# == LRGeneratedTable == +# +# This class implements the LR table generation algorithm. There are no +# public methods except for write() +# ----------------------------------------------------------------------------- + +class LRGeneratedTable(LRTable): + def __init__(self,grammar,method='LALR',log=None): + if method not in ['SLR','LALR']: + raise LALRError("Unsupported method %s" % method) + + self.grammar = grammar + self.lr_method = method + + # Set up the logger + if not log: + log = NullLogger() + self.log = log + + # Internal attributes + self.lr_action = {} # Action table + self.lr_goto = {} # Goto table + self.lr_productions = grammar.Productions # Copy of grammar Production array + self.lr_goto_cache = {} # Cache of computed gotos + self.lr0_cidhash = {} # Cache of closures + + self._add_count = 0 # Internal counter used to detect cycles + + # Diagonistic information filled in by the table generator + self.sr_conflict = 0 + self.rr_conflict = 0 + self.conflicts = [] # List of conflicts + + self.sr_conflicts = [] + self.rr_conflicts = [] + + # Build the tables + self.grammar.build_lritems() + self.grammar.compute_first() + self.grammar.compute_follow() + self.lr_parse_table() + + # Compute the LR(0) closure operation on I, where I is a set of LR(0) items. + + def lr0_closure(self,I): + self._add_count += 1 + + # Add everything in I to J + J = I[:] + didadd = 1 + while didadd: + didadd = 0 + for j in J: + for x in j.lr_after: + if getattr(x,"lr0_added",0) == self._add_count: continue + # Add B --> .G to J + J.append(x.lr_next) + x.lr0_added = self._add_count + didadd = 1 + + return J + + # Compute the LR(0) goto function goto(I,X) where I is a set + # of LR(0) items and X is a grammar symbol. This function is written + # in a way that guarantees uniqueness of the generated goto sets + # (i.e. the same goto set will never be returned as two different Python + # objects). With uniqueness, we can later do fast set comparisons using + # id(obj) instead of element-wise comparison. + + def lr0_goto(self,I,x): + # First we look for a previously cached entry + g = self.lr_goto_cache.get((id(I),x),None) + if g: return g + + # Now we generate the goto set in a way that guarantees uniqueness + # of the result + + s = self.lr_goto_cache.get(x,None) + if not s: + s = { } + self.lr_goto_cache[x] = s + + gs = [ ] + for p in I: + n = p.lr_next + if n and n.lr_before == x: + s1 = s.get(id(n),None) + if not s1: + s1 = { } + s[id(n)] = s1 + gs.append(n) + s = s1 + g = s.get('$end',None) + if not g: + if gs: + g = self.lr0_closure(gs) + s['$end'] = g + else: + s['$end'] = gs + self.lr_goto_cache[(id(I),x)] = g + return g + + # Compute the LR(0) sets of item function + def lr0_items(self): + + C = [ self.lr0_closure([self.grammar.Productions[0].lr_next]) ] + i = 0 + for I in C: + self.lr0_cidhash[id(I)] = i + i += 1 + + # Loop over the items in C and each grammar symbols + i = 0 + while i < len(C): + I = C[i] + i += 1 + + # Collect all of the symbols that could possibly be in the goto(I,X) sets + asyms = { } + for ii in I: + for s in ii.usyms: + asyms[s] = None + + for x in asyms: + g = self.lr0_goto(I,x) + if not g: continue + if id(g) in self.lr0_cidhash: continue + self.lr0_cidhash[id(g)] = len(C) + C.append(g) + + return C + + # ----------------------------------------------------------------------------- + # ==== LALR(1) Parsing ==== + # + # LALR(1) parsing is almost exactly the same as SLR except that instead of + # relying upon Follow() sets when performing reductions, a more selective + # lookahead set that incorporates the state of the LR(0) machine is utilized. + # Thus, we mainly just have to focus on calculating the lookahead sets. + # + # The method used here is due to DeRemer and Pennelo (1982). + # + # DeRemer, F. L., and T. J. Pennelo: "Efficient Computation of LALR(1) + # Lookahead Sets", ACM Transactions on Programming Languages and Systems, + # Vol. 4, No. 4, Oct. 1982, pp. 615-649 + # + # Further details can also be found in: + # + # J. Tremblay and P. Sorenson, "The Theory and Practice of Compiler Writing", + # McGraw-Hill Book Company, (1985). + # + # ----------------------------------------------------------------------------- + + # ----------------------------------------------------------------------------- + # compute_nullable_nonterminals() + # + # Creates a dictionary containing all of the non-terminals that might produce + # an empty production. + # ----------------------------------------------------------------------------- + + def compute_nullable_nonterminals(self): + nullable = {} + num_nullable = 0 + while 1: + for p in self.grammar.Productions[1:]: + if p.len == 0: + nullable[p.name] = 1 + continue + for t in p.prod: + if not t in nullable: break + else: + nullable[p.name] = 1 + if len(nullable) == num_nullable: break + num_nullable = len(nullable) + return nullable + + # ----------------------------------------------------------------------------- + # find_nonterminal_trans(C) + # + # Given a set of LR(0) items, this functions finds all of the non-terminal + # transitions. These are transitions in which a dot appears immediately before + # a non-terminal. Returns a list of tuples of the form (state,N) where state + # is the state number and N is the nonterminal symbol. + # + # The input C is the set of LR(0) items. + # ----------------------------------------------------------------------------- + + def find_nonterminal_transitions(self,C): + trans = [] + for state in range(len(C)): + for p in C[state]: + if p.lr_index < p.len - 1: + t = (state,p.prod[p.lr_index+1]) + if t[1] in self.grammar.Nonterminals: + if t not in trans: trans.append(t) + state = state + 1 + return trans + + # ----------------------------------------------------------------------------- + # dr_relation() + # + # Computes the DR(p,A) relationships for non-terminal transitions. The input + # is a tuple (state,N) where state is a number and N is a nonterminal symbol. + # + # Returns a list of terminals. + # ----------------------------------------------------------------------------- + + def dr_relation(self,C,trans,nullable): + dr_set = { } + state,N = trans + terms = [] + + g = self.lr0_goto(C[state],N) + for p in g: + if p.lr_index < p.len - 1: + a = p.prod[p.lr_index+1] + if a in self.grammar.Terminals: + if a not in terms: terms.append(a) + + # This extra bit is to handle the start state + if state == 0 and N == self.grammar.Productions[0].prod[0]: + terms.append('$end') + + return terms + + # ----------------------------------------------------------------------------- + # reads_relation() + # + # Computes the READS() relation (p,A) READS (t,C). + # ----------------------------------------------------------------------------- + + def reads_relation(self,C, trans, empty): + # Look for empty transitions + rel = [] + state, N = trans + + g = self.lr0_goto(C[state],N) + j = self.lr0_cidhash.get(id(g),-1) + for p in g: + if p.lr_index < p.len - 1: + a = p.prod[p.lr_index + 1] + if a in empty: + rel.append((j,a)) + + return rel + + # ----------------------------------------------------------------------------- + # compute_lookback_includes() + # + # Determines the lookback and includes relations + # + # LOOKBACK: + # + # This relation is determined by running the LR(0) state machine forward. + # For example, starting with a production "N : . A B C", we run it forward + # to obtain "N : A B C ." We then build a relationship between this final + # state and the starting state. These relationships are stored in a dictionary + # lookdict. + # + # INCLUDES: + # + # Computes the INCLUDE() relation (p,A) INCLUDES (p',B). + # + # This relation is used to determine non-terminal transitions that occur + # inside of other non-terminal transition states. (p,A) INCLUDES (p', B) + # if the following holds: + # + # B -> LAT, where T -> epsilon and p' -L-> p + # + # L is essentially a prefix (which may be empty), T is a suffix that must be + # able to derive an empty string. State p' must lead to state p with the string L. + # + # ----------------------------------------------------------------------------- + + def compute_lookback_includes(self,C,trans,nullable): + + lookdict = {} # Dictionary of lookback relations + includedict = {} # Dictionary of include relations + + # Make a dictionary of non-terminal transitions + dtrans = {} + for t in trans: + dtrans[t] = 1 + + # Loop over all transitions and compute lookbacks and includes + for state,N in trans: + lookb = [] + includes = [] + for p in C[state]: + if p.name != N: continue + + # Okay, we have a name match. We now follow the production all the way + # through the state machine until we get the . on the right hand side + + lr_index = p.lr_index + j = state + while lr_index < p.len - 1: + lr_index = lr_index + 1 + t = p.prod[lr_index] + + # Check to see if this symbol and state are a non-terminal transition + if (j,t) in dtrans: + # Yes. Okay, there is some chance that this is an includes relation + # the only way to know for certain is whether the rest of the + # production derives empty + + li = lr_index + 1 + while li < p.len: + if p.prod[li] in self.grammar.Terminals: break # No forget it + if not p.prod[li] in nullable: break + li = li + 1 + else: + # Appears to be a relation between (j,t) and (state,N) + includes.append((j,t)) + + g = self.lr0_goto(C[j],t) # Go to next set + j = self.lr0_cidhash.get(id(g),-1) # Go to next state + + # When we get here, j is the final state, now we have to locate the production + for r in C[j]: + if r.name != p.name: continue + if r.len != p.len: continue + i = 0 + # This look is comparing a production ". A B C" with "A B C ." + while i < r.lr_index: + if r.prod[i] != p.prod[i+1]: break + i = i + 1 + else: + lookb.append((j,r)) + for i in includes: + if not i in includedict: includedict[i] = [] + includedict[i].append((state,N)) + lookdict[(state,N)] = lookb + + return lookdict,includedict + + # ----------------------------------------------------------------------------- + # compute_read_sets() + # + # Given a set of LR(0) items, this function computes the read sets. + # + # Inputs: C = Set of LR(0) items + # ntrans = Set of nonterminal transitions + # nullable = Set of empty transitions + # + # Returns a set containing the read sets + # ----------------------------------------------------------------------------- + + def compute_read_sets(self,C, ntrans, nullable): + FP = lambda x: self.dr_relation(C,x,nullable) + R = lambda x: self.reads_relation(C,x,nullable) + F = digraph(ntrans,R,FP) + return F + + # ----------------------------------------------------------------------------- + # compute_follow_sets() + # + # Given a set of LR(0) items, a set of non-terminal transitions, a readset, + # and an include set, this function computes the follow sets + # + # Follow(p,A) = Read(p,A) U U {Follow(p',B) | (p,A) INCLUDES (p',B)} + # + # Inputs: + # ntrans = Set of nonterminal transitions + # readsets = Readset (previously computed) + # inclsets = Include sets (previously computed) + # + # Returns a set containing the follow sets + # ----------------------------------------------------------------------------- + + def compute_follow_sets(self,ntrans,readsets,inclsets): + FP = lambda x: readsets[x] + R = lambda x: inclsets.get(x,[]) + F = digraph(ntrans,R,FP) + return F + + # ----------------------------------------------------------------------------- + # add_lookaheads() + # + # Attaches the lookahead symbols to grammar rules. + # + # Inputs: lookbacks - Set of lookback relations + # followset - Computed follow set + # + # This function directly attaches the lookaheads to productions contained + # in the lookbacks set + # ----------------------------------------------------------------------------- + + def add_lookaheads(self,lookbacks,followset): + for trans,lb in lookbacks.items(): + # Loop over productions in lookback + for state,p in lb: + if not state in p.lookaheads: + p.lookaheads[state] = [] + f = followset.get(trans,[]) + for a in f: + if a not in p.lookaheads[state]: p.lookaheads[state].append(a) + + # ----------------------------------------------------------------------------- + # add_lalr_lookaheads() + # + # This function does all of the work of adding lookahead information for use + # with LALR parsing + # ----------------------------------------------------------------------------- + + def add_lalr_lookaheads(self,C): + # Determine all of the nullable nonterminals + nullable = self.compute_nullable_nonterminals() + + # Find all non-terminal transitions + trans = self.find_nonterminal_transitions(C) + + # Compute read sets + readsets = self.compute_read_sets(C,trans,nullable) + + # Compute lookback/includes relations + lookd, included = self.compute_lookback_includes(C,trans,nullable) + + # Compute LALR FOLLOW sets + followsets = self.compute_follow_sets(trans,readsets,included) + + # Add all of the lookaheads + self.add_lookaheads(lookd,followsets) + + # ----------------------------------------------------------------------------- + # lr_parse_table() + # + # This function constructs the parse tables for SLR or LALR + # ----------------------------------------------------------------------------- + def lr_parse_table(self): + Productions = self.grammar.Productions + Precedence = self.grammar.Precedence + goto = self.lr_goto # Goto array + action = self.lr_action # Action array + log = self.log # Logger for output + + actionp = { } # Action production array (temporary) + + log.info("Parsing method: %s", self.lr_method) + + # Step 1: Construct C = { I0, I1, ... IN}, collection of LR(0) items + # This determines the number of states + + C = self.lr0_items() + + if self.lr_method == 'LALR': + self.add_lalr_lookaheads(C) + + # Build the parser table, state by state + st = 0 + for I in C: + # Loop over each production in I + actlist = [ ] # List of actions + st_action = { } + st_actionp = { } + st_goto = { } + log.info("") + log.info("state %d", st) + log.info("") + for p in I: + log.info(" (%d) %s", p.number, str(p)) + log.info("") + + for p in I: + if p.len == p.lr_index + 1: + if p.name == "S'": + # Start symbol. Accept! + st_action["$end"] = 0 + st_actionp["$end"] = p + else: + # We are at the end of a production. Reduce! + if self.lr_method == 'LALR': + laheads = p.lookaheads[st] + else: + laheads = self.grammar.Follow[p.name] + for a in laheads: + actlist.append((a,p,"reduce using rule %d (%s)" % (p.number,p))) + r = st_action.get(a,None) + if r is not None: + # Whoa. Have a shift/reduce or reduce/reduce conflict + if r > 0: + # Need to decide on shift or reduce here + # By default we favor shifting. Need to add + # some precedence rules here. + sprec,slevel = Productions[st_actionp[a].number].prec + rprec,rlevel = Precedence.get(a,('right',0)) + if (slevel < rlevel) or ((slevel == rlevel) and (rprec == 'left')): + # We really need to reduce here. + st_action[a] = -p.number + st_actionp[a] = p + if not slevel and not rlevel: + log.info(" ! shift/reduce conflict for %s resolved as reduce",a) + self.sr_conflicts.append((st,a,'reduce')) + Productions[p.number].reduced += 1 + elif (slevel == rlevel) and (rprec == 'nonassoc'): + st_action[a] = None + else: + # Hmmm. Guess we'll keep the shift + if not rlevel: + log.info(" ! shift/reduce conflict for %s resolved as shift",a) + self.sr_conflicts.append((st,a,'shift')) + elif r < 0: + # Reduce/reduce conflict. In this case, we favor the rule + # that was defined first in the grammar file + oldp = Productions[-r] + pp = Productions[p.number] + if oldp.line > pp.line: + st_action[a] = -p.number + st_actionp[a] = p + chosenp,rejectp = pp,oldp + Productions[p.number].reduced += 1 + Productions[oldp.number].reduced -= 1 + else: + chosenp,rejectp = oldp,pp + self.rr_conflicts.append((st,chosenp,rejectp)) + log.info(" ! reduce/reduce conflict for %s resolved using rule %d (%s)", a,st_actionp[a].number, st_actionp[a]) + else: + raise LALRError("Unknown conflict in state %d" % st) + else: + st_action[a] = -p.number + st_actionp[a] = p + Productions[p.number].reduced += 1 + else: + i = p.lr_index + a = p.prod[i+1] # Get symbol right after the "." + if a in self.grammar.Terminals: + g = self.lr0_goto(I,a) + j = self.lr0_cidhash.get(id(g),-1) + if j >= 0: + # We are in a shift state + actlist.append((a,p,"shift and go to state %d" % j)) + r = st_action.get(a,None) + if r is not None: + # Whoa have a shift/reduce or shift/shift conflict + if r > 0: + if r != j: + raise LALRError("Shift/shift conflict in state %d" % st) + elif r < 0: + # Do a precedence check. + # - if precedence of reduce rule is higher, we reduce. + # - if precedence of reduce is same and left assoc, we reduce. + # - otherwise we shift + rprec,rlevel = Productions[st_actionp[a].number].prec + sprec,slevel = Precedence.get(a,('right',0)) + if (slevel > rlevel) or ((slevel == rlevel) and (rprec == 'right')): + # We decide to shift here... highest precedence to shift + Productions[st_actionp[a].number].reduced -= 1 + st_action[a] = j + st_actionp[a] = p + if not rlevel: + log.info(" ! shift/reduce conflict for %s resolved as shift",a) + self.sr_conflicts.append((st,a,'shift')) + elif (slevel == rlevel) and (rprec == 'nonassoc'): + st_action[a] = None + else: + # Hmmm. Guess we'll keep the reduce + if not slevel and not rlevel: + log.info(" ! shift/reduce conflict for %s resolved as reduce",a) + self.sr_conflicts.append((st,a,'reduce')) + + else: + raise LALRError("Unknown conflict in state %d" % st) + else: + st_action[a] = j + st_actionp[a] = p + + # Print the actions associated with each terminal + _actprint = { } + for a,p,m in actlist: + if a in st_action: + if p is st_actionp[a]: + log.info(" %-15s %s",a,m) + _actprint[(a,m)] = 1 + log.info("") + # Print the actions that were not used. (debugging) + not_used = 0 + for a,p,m in actlist: + if a in st_action: + if p is not st_actionp[a]: + if not (a,m) in _actprint: + log.debug(" ! %-15s [ %s ]",a,m) + not_used = 1 + _actprint[(a,m)] = 1 + if not_used: + log.debug("") + + # Construct the goto table for this state + + nkeys = { } + for ii in I: + for s in ii.usyms: + if s in self.grammar.Nonterminals: + nkeys[s] = None + for n in nkeys: + g = self.lr0_goto(I,n) + j = self.lr0_cidhash.get(id(g),-1) + if j >= 0: + st_goto[n] = j + log.info(" %-30s shift and go to state %d",n,j) + + action[st] = st_action + actionp[st] = st_actionp + goto[st] = st_goto + st += 1 + + + # ----------------------------------------------------------------------------- + # write() + # + # This function writes the LR parsing tables to a file + # ----------------------------------------------------------------------------- + + def write_table(self,modulename,outputdir='',signature=""): + basemodulename = modulename.split(".")[-1] + filename = os.path.join(outputdir,basemodulename) + ".py" + try: + f = open(filename,"w") + + f.write(""" +# %s +# This file is automatically generated. Do not edit. +_tabversion = %r + +_lr_method = %r + +_lr_signature = %r + """ % (filename, __tabversion__, self.lr_method, signature)) + + # Change smaller to 0 to go back to original tables + smaller = 1 + + # Factor out names to try and make smaller + if smaller: + items = { } + + for s,nd in self.lr_action.items(): + for name,v in nd.items(): + i = items.get(name) + if not i: + i = ([],[]) + items[name] = i + i[0].append(s) + i[1].append(v) + + f.write("\n_lr_action_items = {") + for k,v in items.items(): + f.write("%r:([" % k) + for i in v[0]: + f.write("%r," % i) + f.write("],[") + for i in v[1]: + f.write("%r," % i) + + f.write("]),") + f.write("}\n") + + f.write(""" +_lr_action = { } +for _k, _v in _lr_action_items.items(): + for _x,_y in zip(_v[0],_v[1]): + if not _x in _lr_action: _lr_action[_x] = { } + _lr_action[_x][_k] = _y +del _lr_action_items +""") + + else: + f.write("\n_lr_action = { "); + for k,v in self.lr_action.items(): + f.write("(%r,%r):%r," % (k[0],k[1],v)) + f.write("}\n"); + + if smaller: + # Factor out names to try and make smaller + items = { } + + for s,nd in self.lr_goto.items(): + for name,v in nd.items(): + i = items.get(name) + if not i: + i = ([],[]) + items[name] = i + i[0].append(s) + i[1].append(v) + + f.write("\n_lr_goto_items = {") + for k,v in items.items(): + f.write("%r:([" % k) + for i in v[0]: + f.write("%r," % i) + f.write("],[") + for i in v[1]: + f.write("%r," % i) + + f.write("]),") + f.write("}\n") + + f.write(""" +_lr_goto = { } +for _k, _v in _lr_goto_items.items(): + for _x,_y in zip(_v[0],_v[1]): + if not _x in _lr_goto: _lr_goto[_x] = { } + _lr_goto[_x][_k] = _y +del _lr_goto_items +""") + else: + f.write("\n_lr_goto = { "); + for k,v in self.lr_goto.items(): + f.write("(%r,%r):%r," % (k[0],k[1],v)) + f.write("}\n"); + + # Write production table + f.write("_lr_productions = [\n") + for p in self.lr_productions: + if p.func: + f.write(" (%r,%r,%d,%r,%r,%d),\n" % (p.str,p.name, p.len, p.func,p.file,p.line)) + else: + f.write(" (%r,%r,%d,None,None,None),\n" % (str(p),p.name, p.len)) + f.write("]\n") + f.close() + + except IOError: + e = sys.exc_info()[1] + sys.stderr.write("Unable to create '%s'\n" % filename) + sys.stderr.write(str(e)+"\n") + return + + + # ----------------------------------------------------------------------------- + # pickle_table() + # + # This function pickles the LR parsing tables to a supplied file object + # ----------------------------------------------------------------------------- + + def pickle_table(self,filename,signature=""): + try: + import cPickle as pickle + except ImportError: + import pickle + outf = open(filename,"wb") + pickle.dump(__tabversion__,outf,pickle_protocol) + pickle.dump(self.lr_method,outf,pickle_protocol) + pickle.dump(signature,outf,pickle_protocol) + pickle.dump(self.lr_action,outf,pickle_protocol) + pickle.dump(self.lr_goto,outf,pickle_protocol) + + outp = [] + for p in self.lr_productions: + if p.func: + outp.append((p.str,p.name, p.len, p.func,p.file,p.line)) + else: + outp.append((str(p),p.name,p.len,None,None,None)) + pickle.dump(outp,outf,pickle_protocol) + outf.close() + +# ----------------------------------------------------------------------------- +# === INTROSPECTION === +# +# The following functions and classes are used to implement the PLY +# introspection features followed by the yacc() function itself. +# ----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- +# get_caller_module_dict() +# +# This function returns a dictionary containing all of the symbols defined within +# a caller further down the call stack. This is used to get the environment +# associated with the yacc() call if none was provided. +# ----------------------------------------------------------------------------- + +def get_caller_module_dict(levels): + try: + raise RuntimeError + except RuntimeError: + e,b,t = sys.exc_info() + f = t.tb_frame + while levels > 0: + f = f.f_back + levels -= 1 + ldict = f.f_globals.copy() + if f.f_globals != f.f_locals: + ldict.update(f.f_locals) + + return ldict + +# ----------------------------------------------------------------------------- +# parse_grammar() +# +# This takes a raw grammar rule string and parses it into production data +# ----------------------------------------------------------------------------- +def parse_grammar(doc,file,line): + grammar = [] + # Split the doc string into lines + pstrings = doc.splitlines() + lastp = None + dline = line + for ps in pstrings: + dline += 1 + p = ps.split() + if not p: continue + try: + if p[0] == '|': + # This is a continuation of a previous rule + if not lastp: + raise SyntaxError("%s:%d: Misplaced '|'" % (file,dline)) + prodname = lastp + syms = p[1:] + else: + prodname = p[0] + lastp = prodname + syms = p[2:] + assign = p[1] + if assign != ':' and assign != '::=': + raise SyntaxError("%s:%d: Syntax error. Expected ':'" % (file,dline)) + + grammar.append((file,dline,prodname,syms)) + except SyntaxError: + raise + except Exception: + raise SyntaxError("%s:%d: Syntax error in rule '%s'" % (file,dline,ps.strip())) + + return grammar + +# ----------------------------------------------------------------------------- +# ParserReflect() +# +# This class represents information extracted for building a parser including +# start symbol, error function, tokens, precedence list, action functions, +# etc. +# ----------------------------------------------------------------------------- +class ParserReflect(object): + def __init__(self,pdict,log=None): + self.pdict = pdict + self.start = None + self.error_func = None + self.tokens = None + self.files = {} + self.grammar = [] + self.error = 0 + + if log is None: + self.log = PlyLogger(sys.stderr) + else: + self.log = log + + # Get all of the basic information + def get_all(self): + self.get_start() + self.get_error_func() + self.get_tokens() + self.get_precedence() + self.get_pfunctions() + + # Validate all of the information + def validate_all(self): + self.validate_start() + self.validate_error_func() + self.validate_tokens() + self.validate_precedence() + self.validate_pfunctions() + self.validate_files() + return self.error + + # Compute a signature over the grammar + def signature(self): + try: + from hashlib import md5 + except ImportError: + from md5 import md5 + try: + sig = md5() + if self.start: + sig.update(self.start.encode('latin-1')) + if self.prec: + sig.update("".join(["".join(p) for p in self.prec]).encode('latin-1')) + if self.tokens: + sig.update(" ".join(self.tokens).encode('latin-1')) + for f in self.pfuncs: + if f[3]: + sig.update(f[3].encode('latin-1')) + except (TypeError,ValueError): + pass + return sig.digest() + + # ----------------------------------------------------------------------------- + # validate_file() + # + # This method checks to see if there are duplicated p_rulename() functions + # in the parser module file. Without this function, it is really easy for + # users to make mistakes by cutting and pasting code fragments (and it's a real + # bugger to try and figure out why the resulting parser doesn't work). Therefore, + # we just do a little regular expression pattern matching of def statements + # to try and detect duplicates. + # ----------------------------------------------------------------------------- + + def validate_files(self): + # Match def p_funcname( + fre = re.compile(r'\s*def\s+(p_[a-zA-Z_0-9]*)\(') + + for filename in self.files.keys(): + base,ext = os.path.splitext(filename) + if ext != '.py': return 1 # No idea. Assume it's okay. + + try: + f = open(filename) + lines = f.readlines() + f.close() + except IOError: + continue + + counthash = { } + for linen,l in enumerate(lines): + linen += 1 + m = fre.match(l) + if m: + name = m.group(1) + prev = counthash.get(name) + if not prev: + counthash[name] = linen + else: + self.log.warning("%s:%d: Function %s redefined. Previously defined on line %d", filename,linen,name,prev) + + # Get the start symbol + def get_start(self): + self.start = self.pdict.get('start') + + # Validate the start symbol + def validate_start(self): + if self.start is not None: + if not isinstance(self.start,str): + self.log.error("'start' must be a string") + + # Look for error handler + def get_error_func(self): + self.error_func = self.pdict.get('p_error') + + # Validate the error function + def validate_error_func(self): + if self.error_func: + if isinstance(self.error_func,types.FunctionType): + ismethod = 0 + elif isinstance(self.error_func, types.MethodType): + ismethod = 1 + else: + self.log.error("'p_error' defined, but is not a function or method") + self.error = 1 + return + + eline = func_code(self.error_func).co_firstlineno + efile = func_code(self.error_func).co_filename + self.files[efile] = 1 + + if (func_code(self.error_func).co_argcount != 1+ismethod): + self.log.error("%s:%d: p_error() requires 1 argument",efile,eline) + self.error = 1 + + # Get the tokens map + def get_tokens(self): + tokens = self.pdict.get("tokens",None) + if not tokens: + self.log.error("No token list is defined") + self.error = 1 + return + + if not isinstance(tokens,(list, tuple)): + self.log.error("tokens must be a list or tuple") + self.error = 1 + return + + if not tokens: + self.log.error("tokens is empty") + self.error = 1 + return + + self.tokens = tokens + + # Validate the tokens + def validate_tokens(self): + # Validate the tokens. + if 'error' in self.tokens: + self.log.error("Illegal token name 'error'. Is a reserved word") + self.error = 1 + return + + terminals = {} + for n in self.tokens: + if n in terminals: + self.log.warning("Token '%s' multiply defined", n) + terminals[n] = 1 + + # Get the precedence map (if any) + def get_precedence(self): + self.prec = self.pdict.get("precedence",None) + + # Validate and parse the precedence map + def validate_precedence(self): + preclist = [] + if self.prec: + if not isinstance(self.prec,(list,tuple)): + self.log.error("precedence must be a list or tuple") + self.error = 1 + return + for level,p in enumerate(self.prec): + if not isinstance(p,(list,tuple)): + self.log.error("Bad precedence table") + self.error = 1 + return + + if len(p) < 2: + self.log.error("Malformed precedence entry %s. Must be (assoc, term, ..., term)",p) + self.error = 1 + return + assoc = p[0] + if not isinstance(assoc,str): + self.log.error("precedence associativity must be a string") + self.error = 1 + return + for term in p[1:]: + if not isinstance(term,str): + self.log.error("precedence items must be strings") + self.error = 1 + return + preclist.append((term,assoc,level+1)) + self.preclist = preclist + + # Get all p_functions from the grammar + def get_pfunctions(self): + p_functions = [] + for name, item in self.pdict.items(): + if name[:2] != 'p_': continue + if name == 'p_error': continue + if isinstance(item,(types.FunctionType,types.MethodType)): + line = func_code(item).co_firstlineno + file = func_code(item).co_filename + p_functions.append((line,file,name,item.__doc__)) + + # Sort all of the actions by line number + p_functions.sort() + self.pfuncs = p_functions + + + # Validate all of the p_functions + def validate_pfunctions(self): + grammar = [] + # Check for non-empty symbols + if len(self.pfuncs) == 0: + self.log.error("no rules of the form p_rulename are defined") + self.error = 1 + return + + for line, file, name, doc in self.pfuncs: + func = self.pdict[name] + if isinstance(func, types.MethodType): + reqargs = 2 + else: + reqargs = 1 + if func_code(func).co_argcount > reqargs: + self.log.error("%s:%d: Rule '%s' has too many arguments",file,line,func.__name__) + self.error = 1 + elif func_code(func).co_argcount < reqargs: + self.log.error("%s:%d: Rule '%s' requires an argument",file,line,func.__name__) + self.error = 1 + elif not func.__doc__: + self.log.warning("%s:%d: No documentation string specified in function '%s' (ignored)",file,line,func.__name__) + else: + try: + parsed_g = parse_grammar(doc,file,line) + for g in parsed_g: + grammar.append((name, g)) + except SyntaxError: + e = sys.exc_info()[1] + self.log.error(str(e)) + self.error = 1 + + # Looks like a valid grammar rule + # Mark the file in which defined. + self.files[file] = 1 + + # Secondary validation step that looks for p_ definitions that are not functions + # or functions that look like they might be grammar rules. + + for n,v in self.pdict.items(): + if n[0:2] == 'p_' and isinstance(v, (types.FunctionType, types.MethodType)): continue + if n[0:2] == 't_': continue + if n[0:2] == 'p_' and n != 'p_error': + self.log.warning("'%s' not defined as a function", n) + if ((isinstance(v,types.FunctionType) and func_code(v).co_argcount == 1) or + (isinstance(v,types.MethodType) and func_code(v).co_argcount == 2)): + try: + doc = v.__doc__.split(" ") + if doc[1] == ':': + self.log.warning("%s:%d: Possible grammar rule '%s' defined without p_ prefix", + func_code(v).co_filename, func_code(v).co_firstlineno,n) + except Exception: + pass + + self.grammar = grammar + +# ----------------------------------------------------------------------------- +# yacc(module) +# +# Build a parser +# ----------------------------------------------------------------------------- + +def yacc(method='LALR', debug=yaccdebug, module=None, tabmodule=tab_module, start=None, + check_recursion=1, optimize=0, write_tables=1, debugfile=debug_file,outputdir='', + debuglog=None, errorlog = None, picklefile=None): + + global parse # Reference to the parsing method of the last built parser + + # If pickling is enabled, table files are not created + + if picklefile: + write_tables = 0 + + if errorlog is None: + errorlog = PlyLogger(sys.stderr) + + # Get the module dictionary used for the parser + if module: + _items = [(k,getattr(module,k)) for k in dir(module)] + pdict = dict(_items) + else: + pdict = get_caller_module_dict(2) + + # Collect parser information from the dictionary + pinfo = ParserReflect(pdict,log=errorlog) + pinfo.get_all() + + if pinfo.error: + raise YaccError("Unable to build parser") + + # Check signature against table files (if any) + signature = pinfo.signature() + + # Read the tables + try: + lr = LRTable() + if picklefile: + read_signature = lr.read_pickle(picklefile) + else: + read_signature = lr.read_table(tabmodule) + if optimize or (read_signature == signature): + try: + lr.bind_callables(pinfo.pdict) + parser = LRParser(lr,pinfo.error_func) + parse = parser.parse + return parser + except Exception: + e = sys.exc_info()[1] + errorlog.warning("There was a problem loading the table file: %s", repr(e)) + except VersionError: + e = sys.exc_info() + errorlog.warning(str(e)) + except Exception: + pass + + if debuglog is None: + if debug: + debuglog = PlyLogger(open(debugfile,"w")) + else: + debuglog = NullLogger() + + debuglog.info("Created by PLY version %s (http://www.dabeaz.com/ply)", __version__) + + + errors = 0 + + # Validate the parser information + if pinfo.validate_all(): + raise YaccError("Unable to build parser") + + if not pinfo.error_func: + errorlog.warning("no p_error() function is defined") + + # Create a grammar object + grammar = Grammar(pinfo.tokens) + + # Set precedence level for terminals + for term, assoc, level in pinfo.preclist: + try: + grammar.set_precedence(term,assoc,level) + except GrammarError: + e = sys.exc_info()[1] + errorlog.warning("%s",str(e)) + + # Add productions to the grammar + for funcname, gram in pinfo.grammar: + file, line, prodname, syms = gram + try: + grammar.add_production(prodname,syms,funcname,file,line) + except GrammarError: + e = sys.exc_info()[1] + errorlog.error("%s",str(e)) + errors = 1 + + # Set the grammar start symbols + try: + if start is None: + grammar.set_start(pinfo.start) + else: + grammar.set_start(start) + except GrammarError: + e = sys.exc_info()[1] + errorlog.error(str(e)) + errors = 1 + + if errors: + raise YaccError("Unable to build parser") + + # Verify the grammar structure + undefined_symbols = grammar.undefined_symbols() + for sym, prod in undefined_symbols: + errorlog.error("%s:%d: Symbol '%s' used, but not defined as a token or a rule",prod.file,prod.line,sym) + errors = 1 + + unused_terminals = grammar.unused_terminals() + if unused_terminals: + debuglog.info("") + debuglog.info("Unused terminals:") + debuglog.info("") + for term in unused_terminals: + errorlog.warning("Token '%s' defined, but not used", term) + debuglog.info(" %s", term) + + # Print out all productions to the debug log + if debug: + debuglog.info("") + debuglog.info("Grammar") + debuglog.info("") + for n,p in enumerate(grammar.Productions): + debuglog.info("Rule %-5d %s", n, p) + + # Find unused non-terminals + unused_rules = grammar.unused_rules() + for prod in unused_rules: + errorlog.warning("%s:%d: Rule '%s' defined, but not used", prod.file, prod.line, prod.name) + + if len(unused_terminals) == 1: + errorlog.warning("There is 1 unused token") + if len(unused_terminals) > 1: + errorlog.warning("There are %d unused tokens", len(unused_terminals)) + + if len(unused_rules) == 1: + errorlog.warning("There is 1 unused rule") + if len(unused_rules) > 1: + errorlog.warning("There are %d unused rules", len(unused_rules)) + + if debug: + debuglog.info("") + debuglog.info("Terminals, with rules where they appear") + debuglog.info("") + terms = list(grammar.Terminals) + terms.sort() + for term in terms: + debuglog.info("%-20s : %s", term, " ".join([str(s) for s in grammar.Terminals[term]])) + + debuglog.info("") + debuglog.info("Nonterminals, with rules where they appear") + debuglog.info("") + nonterms = list(grammar.Nonterminals) + nonterms.sort() + for nonterm in nonterms: + debuglog.info("%-20s : %s", nonterm, " ".join([str(s) for s in grammar.Nonterminals[nonterm]])) + debuglog.info("") + + if check_recursion: + unreachable = grammar.find_unreachable() + for u in unreachable: + errorlog.warning("Symbol '%s' is unreachable",u) + + infinite = grammar.infinite_cycles() + for inf in infinite: + errorlog.error("Infinite recursion detected for symbol '%s'", inf) + errors = 1 + + unused_prec = grammar.unused_precedence() + for term, assoc in unused_prec: + errorlog.error("Precedence rule '%s' defined for unknown symbol '%s'", assoc, term) + errors = 1 + + if errors: + raise YaccError("Unable to build parser") + + # Run the LRGeneratedTable on the grammar + if debug: + errorlog.debug("Generating %s tables", method) + + lr = LRGeneratedTable(grammar,method,debuglog) + + if debug: + num_sr = len(lr.sr_conflicts) + + # Report shift/reduce and reduce/reduce conflicts + if num_sr == 1: + errorlog.warning("1 shift/reduce conflict") + elif num_sr > 1: + errorlog.warning("%d shift/reduce conflicts", num_sr) + + num_rr = len(lr.rr_conflicts) + if num_rr == 1: + errorlog.warning("1 reduce/reduce conflict") + elif num_rr > 1: + errorlog.warning("%d reduce/reduce conflicts", num_rr) + + # Write out conflicts to the output file + if debug and (lr.sr_conflicts or lr.rr_conflicts): + debuglog.warning("") + debuglog.warning("Conflicts:") + debuglog.warning("") + + for state, tok, resolution in lr.sr_conflicts: + debuglog.warning("shift/reduce conflict for %s in state %d resolved as %s", tok, state, resolution) + + already_reported = {} + for state, rule, rejected in lr.rr_conflicts: + if (state,id(rule),id(rejected)) in already_reported: + continue + debuglog.warning("reduce/reduce conflict in state %d resolved using rule (%s)", state, rule) + debuglog.warning("rejected rule (%s) in state %d", rejected,state) + errorlog.warning("reduce/reduce conflict in state %d resolved using rule (%s)", state, rule) + errorlog.warning("rejected rule (%s) in state %d", rejected, state) + already_reported[state,id(rule),id(rejected)] = 1 + + warned_never = [] + for state, rule, rejected in lr.rr_conflicts: + if not rejected.reduced and (rejected not in warned_never): + debuglog.warning("Rule (%s) is never reduced", rejected) + errorlog.warning("Rule (%s) is never reduced", rejected) + warned_never.append(rejected) + + # Write the table file if requested + if write_tables: + lr.write_table(tabmodule,outputdir,signature) + + # Write a pickled version of the tables + if picklefile: + lr.pickle_table(picklefile,signature) + + # Build the parser + lr.bind_callables(pinfo.pdict) + parser = LRParser(lr,pinfo.error_func) + + parse = parser.parse + return parser diff --git a/mojo/public/tools/bindings/generators/cpp_templates/enum_macros.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/enum_macros.tmpl new file mode 100644 index 0000000..0e44acd --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/enum_macros.tmpl @@ -0,0 +1,46 @@ +{#--- + Macro for enum definition, and the declaration of associated functions. + `is_static` is relevant if this enum declaration is defined within a class, in + which case associated functions need to be static. +---#} +{%- macro enum_decl(enum, is_static=false) %} +enum {{enum.name}} : int32_t { +{%- for field in enum.fields %} +{%- if field.value %} + {{enum.name|to_all_caps}}_{{field.name}} = {{field.value|expression_to_text}}, +{%- else %} + {{enum.name|to_all_caps}}_{{field.name}}, +{%- endif %} +{%- endfor %} +}; +{{is_valid_enum_decl(enum, is_static)}} +{%- endmacro %} + +{#--- macros for the declaration & definitions of enum-associated functions. + Namely: + * {enum_name}_IsValidValue: returns true if the given enum has a valid value + for this generated version of enum. +---#} + +{%- macro is_valid_enum_decl(enum, is_static=false) %} +{% if is_static %}static {% endif -%} +bool {{enum.name}}_IsValidValue({{enum.name}} value); +{%- endmacro %} + +{%- macro is_valid_enum_def(enum, class_name = '') %} +{% if class_name != '' -%} +// static +bool {{class_name}}:: +{%- else -%} +{{"bool "}} +{%- endif -%} +{{enum.name}}_IsValidValue({{enum.name}} value) { + switch (static_cast<int32_t>(value)) { +{%- for enum_field in enum.fields|groupby('numeric_value') %} + case {{enum_field[0]}}: +{%- endfor %} + return true; + } + return false; +} +{%- endmacro %} diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl new file mode 100644 index 0000000..9edc1db --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl @@ -0,0 +1,49 @@ +{%- import "interface_macros.tmpl" as interface_macros %} +class {{interface.name}}Proxy; +class {{interface.name}}Stub; + +class {{interface.name}}RequestValidator; +{%- if interface|has_callbacks %} +class {{interface.name}}ResponseValidator; +{%- endif %} + +class {{interface.name}} { + public: + static const char Name_[]; + static const uint32_t Version_ = {{interface.version}}; + + using Proxy_ = {{interface.name}}Proxy; + using Stub_ = {{interface.name}}Stub; + + using RequestValidator_ = {{interface.name}}RequestValidator; +{%- if interface|has_callbacks %} + using ResponseValidator_ = {{interface.name}}ResponseValidator; +{%- else %} + using ResponseValidator_ = mojo::PassThroughFilter; +{%- endif %} + +{#--- Enums #} +{% from "enum_macros.tmpl" import enum_decl -%} +{%- for enum in interface.enums %} + {{enum_decl(enum, is_static=true)|indent(2)}} +{%- endfor %} + +{#--- Constants #} +{%- for constant in interface.constants %} +{%- if constant.kind|is_integral_kind %} + static const {{constant.kind|cpp_pod_type}} {{constant.name}} = {{constant|constant_value}}; +{%- else %} + static const {{constant.kind|cpp_pod_type}} {{constant.name}}; +{%- endif %} +{%- endfor %} + +{#--- Methods #} + virtual ~{{interface.name}}() {} + +{%- for method in interface.methods %} +{% if method.response_parameters != None %} + using {{method.name}}Callback = {{interface_macros.declare_callback(method)}}; +{%- endif %} + virtual void {{method.name}}({{interface_macros.declare_request_params("", method)}}) = 0; +{%- endfor %} +}; diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl new file mode 100644 index 0000000..15464ce --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl @@ -0,0 +1,324 @@ +{%- import "interface_macros.tmpl" as interface_macros %} +{%- import "struct_macros.tmpl" as struct_macros %} +{%- from "enum_macros.tmpl" import is_valid_enum_def %} + +{%- set class_name = interface.name %} +{%- set proxy_name = interface.name ~ "Proxy" %} +{%- set namespace_as_string = "%s"|format(namespace|replace(".","::")) %} + +{%- macro alloc_params(struct) %} +{%- for param in struct.packed.packed_fields_in_ordinal_order %} + {{param.field.kind|cpp_result_type}} p_{{param.field.name}}{}; +{%- endfor %} + {{struct_macros.deserialize(struct, "params", "p_%s")}} +{%- endmacro %} + +{%- macro pass_params(parameters) %} +{%- for param in parameters %} +{%- if param.kind|is_move_only_kind -%} +p_{{param.name}}.Pass() +{%- else -%} +p_{{param.name}} +{%- endif -%} +{%- if not loop.last %}, {% endif %} +{%- endfor %} +{%- endmacro %} + +{%- macro build_message(struct, struct_display_name) -%} + {{struct_macros.serialize(struct, struct_display_name, "in_%s", "params", "builder.buffer()")}} + params->EncodePointersAndHandles(builder.message()->mutable_handles()); +{%- endmacro %} + +{#--- Begin #} +MOJO_STATIC_CONST_MEMBER_DEFINITION const char {{class_name}}::Name_[] = "{{namespace_as_string}}::{{class_name}}"; +MOJO_STATIC_CONST_MEMBER_DEFINITION const uint32_t {{class_name}}::Version_; + +{#--- Constants #} +{%- for constant in interface.constants %} +{%- if constant.kind|is_integral_kind %} +MOJO_STATIC_CONST_MEMBER_DEFINITION const {{constant.kind|cpp_pod_type}} {{interface.name}}::{{constant.name}}; +{%- else %} +MOJO_STATIC_CONST_MEMBER_DEFINITION const {{constant.kind|cpp_pod_type}} {{interface.name}}::{{constant.name}} = {{constant|constant_value}}; +{%- endif %} +{%- endfor %} + +{#--- Enums #} +{%- for enum in interface.enums %} + {{is_valid_enum_def(enum, class_name=interface.name)|indent(2)}} +{%- endfor %} + +{#--- ForwardToCallback definition #} +{%- for method in interface.methods -%} +{%- if method.response_parameters != None %} +class {{class_name}}_{{method.name}}_ForwardToCallback + : public mojo::MessageReceiver { + public: + {{class_name}}_{{method.name}}_ForwardToCallback( + const {{class_name}}::{{method.name}}Callback& callback) + : callback_(callback) { + } + bool Accept(mojo::Message* message) override; + private: + {{class_name}}::{{method.name}}Callback callback_; + MOJO_DISALLOW_COPY_AND_ASSIGN({{class_name}}_{{method.name}}_ForwardToCallback); +}; +bool {{class_name}}_{{method.name}}_ForwardToCallback::Accept( + mojo::Message* message) { + internal::{{class_name}}_{{method.name}}_ResponseParams_Data* params = + reinterpret_cast<internal::{{class_name}}_{{method.name}}_ResponseParams_Data*>( + message->mutable_payload()); + + params->DecodePointersAndHandles(message->mutable_handles()); + {{alloc_params(method.response_param_struct)}} + callback_.Run({{pass_params(method.response_parameters)}}); + return true; +} +{%- endif %} +{%- endfor %} + +{{proxy_name}}::{{proxy_name}}(mojo::MessageReceiverWithResponder* receiver) + : ControlMessageProxy(receiver) { +} + +{#--- Proxy definitions #} + +{%- for method in interface.methods %} +{%- set message_name = + "internal::k%s_%s_Name"|format(interface.name, method.name) %} +{%- set params_struct = method.param_struct %} +{%- set params_description = + "%s.%s request"|format(interface.name, method.name) %} +void {{proxy_name}}::{{method.name}}( + {{interface_macros.declare_request_params("in_", method)}}) { + {{struct_macros.get_serialized_size(params_struct, "in_%s")}} + +{%- if method.response_parameters != None %} + mojo::internal::RequestMessageBuilder builder({{message_name}}, size); +{%- else %} + mojo::internal::MessageBuilder builder({{message_name}}, size); +{%- endif %} + + {{build_message(params_struct, params_description)}} + +{%- if method.response_parameters != None %} + mojo::MessageReceiver* responder = + new {{class_name}}_{{method.name}}_ForwardToCallback(callback); + if (!receiver_->AcceptWithResponder(builder.message(), responder)) + delete responder; +{%- else %} + bool ok = receiver_->Accept(builder.message()); + // This return value may be ignored as !ok implies the Connector has + // encountered an error, which will be visible through other means. + MOJO_ALLOW_UNUSED_LOCAL(ok); +{%- endif %} +} +{%- endfor %} + +{#--- ProxyToResponder definition #} +{%- for method in interface.methods -%} +{%- if method.response_parameters != None %} +{%- set message_name = + "internal::k%s_%s_Name"|format(interface.name, method.name) %} +{%- set response_params_struct = method.response_param_struct %} +{%- set params_description = + "%s.%s response"|format(interface.name, method.name) %} +class {{class_name}}_{{method.name}}_ProxyToResponder + : public {{class_name}}::{{method.name}}Callback::Runnable { + public: + ~{{class_name}}_{{method.name}}_ProxyToResponder() override { + // Is the Mojo application destroying the callback without running it + // and without first closing the pipe? + bool callback_was_dropped = responder_ && responder_->IsValid(); + // If the Callback was dropped then deleting the responder will close + // the pipe so the calling application knows to stop waiting for a reply. + delete responder_; + MOJO_DCHECK(!callback_was_dropped) << "The callback passed to " + "{{class_name}}::{{method.name}}({%- if method.parameters -%}{{pass_params(method.parameters)}}, {% endif -%}callback) " + "was never run."; + } + + {{class_name}}_{{method.name}}_ProxyToResponder( + uint64_t request_id, + mojo::MessageReceiverWithStatus* responder) + : request_id_(request_id), + responder_(responder) { + } + + void Run({{interface_macros.declare_params("in_", method.response_parameters)}}) const override; + + private: + uint64_t request_id_; + mutable mojo::MessageReceiverWithStatus* responder_; + MOJO_DISALLOW_COPY_AND_ASSIGN({{class_name}}_{{method.name}}_ProxyToResponder); +}; +void {{class_name}}_{{method.name}}_ProxyToResponder::Run( + {{interface_macros.declare_params("in_", method.response_parameters)}}) const { + {{struct_macros.get_serialized_size(response_params_struct, "in_%s")}} + mojo::internal::ResponseMessageBuilder builder( + {{message_name}}, size, request_id_); + {{build_message(response_params_struct, params_description)}} + bool ok = responder_->Accept(builder.message()); + MOJO_ALLOW_UNUSED_LOCAL(ok); + // TODO(darin): !ok returned here indicates a malformed message, and that may + // be good reason to close the connection. However, we don't have a way to do + // that from here. We should add a way. + delete responder_; + responder_ = nullptr; +} +{%- endif -%} +{%- endfor %} + +{{class_name}}Stub::{{class_name}}Stub() + : sink_(nullptr), + control_message_handler_({{interface.name}}::Version_) { +} + +{{class_name}}Stub::~{{interface.name}}Stub() {} + +{#--- Stub definition #} + +bool {{class_name}}Stub::Accept(mojo::Message* message) { + if (mojo::internal::ControlMessageHandler::IsControlMessage(message)) + return control_message_handler_.Accept(message); +{%- if interface.methods %} + switch (message->header()->name) { +{%- for method in interface.methods %} + case internal::k{{class_name}}_{{method.name}}_Name: { + mojo::internal::ScopedTaskTracking task_id("mojo.{{namespace_as_string}}.{{class_name}}.{{method.name}}", __FILE__, __LINE__); +{%- if method.response_parameters == None %} + internal::{{class_name}}_{{method.name}}_Params_Data* params = + reinterpret_cast<internal::{{class_name}}_{{method.name}}_Params_Data*>( + message->mutable_payload()); + + params->DecodePointersAndHandles(message->mutable_handles()); + {{alloc_params(method.param_struct)|indent(4)}} + // A null |sink_| means no implementation was bound. + assert(sink_); + TRACE_EVENT0("mojom", "{{class_name}}::{{method.name}}"); + sink_->{{method.name}}({{pass_params(method.parameters)}}); + return true; +{%- else %} + break; +{%- endif %} + } +{%- endfor %} + } +{%- endif %} + return false; +} + +bool {{class_name}}Stub::AcceptWithResponder( + mojo::Message* message, mojo::MessageReceiverWithStatus* responder) { + if (mojo::internal::ControlMessageHandler::IsControlMessage(message)) + return control_message_handler_.AcceptWithResponder(message, responder); +{%- if interface.methods %} + switch (message->header()->name) { +{%- for method in interface.methods %} + case internal::k{{class_name}}_{{method.name}}_Name: { + mojo::internal::ScopedTaskTracking task_id("mojo::{{namespace_as_string}}::{{class_name}}::{{method.name}}", __FILE__, __LINE__); +{%- if method.response_parameters != None %} + internal::{{class_name}}_{{method.name}}_Params_Data* params = + reinterpret_cast<internal::{{class_name}}_{{method.name}}_Params_Data*>( + message->mutable_payload()); + + params->DecodePointersAndHandles(message->mutable_handles()); + {{class_name}}::{{method.name}}Callback::Runnable* runnable = + new {{class_name}}_{{method.name}}_ProxyToResponder( + message->request_id(), responder); + {{class_name}}::{{method.name}}Callback callback(runnable); + {{alloc_params(method.param_struct)|indent(4)}} + // A null |sink_| means no implementation was bound. + assert(sink_); + TRACE_EVENT0("mojom", "{{class_name}}::{{method.name}}"); + sink_->{{method.name}}( +{%- if method.parameters -%}{{pass_params(method.parameters)}}, {% endif -%}callback); + return true; +{%- else %} + break; +{%- endif %} + } +{%- endfor %} + } +{%- endif %} + return false; +} + +{#--- Request validator definitions #} + +{{class_name}}RequestValidator::{{class_name}}RequestValidator( + mojo::MessageReceiver* sink) : MessageFilter(sink) { +} + +bool {{class_name}}RequestValidator::Accept(mojo::Message* message) { + assert(sink_); + + if (mojo::internal::ControlMessageHandler::IsControlMessage(message)) { + if (!mojo::internal::ValidateControlRequest(message)) + return false; + return sink_->Accept(message); + } + + switch (message->header()->name) { +{%- for method in interface.methods %} + case internal::k{{class_name}}_{{method.name}}_Name: { +{%- if method.response_parameters != None %} + if (!mojo::internal::ValidateMessageIsRequestExpectingResponse(message)) + return false; +{%- else %} + if (!mojo::internal::ValidateMessageIsRequestWithoutResponse(message)) + return false; +{%- endif %} + if (!mojo::internal::ValidateMessagePayload< + internal::{{class_name}}_{{method.name}}_Params_Data>(message)) { + return false; + } + return sink_->Accept(message); + } +{%- endfor %} + default: + break; + } + + // Unrecognized message. + ReportValidationError( + mojo::internal::VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD); + return false; +} + +{#--- Response validator definitions #} +{% if interface|has_callbacks %} +{{class_name}}ResponseValidator::{{class_name}}ResponseValidator( + mojo::MessageReceiver* sink) : MessageFilter(sink) { +} + +bool {{class_name}}ResponseValidator::Accept(mojo::Message* message) { + assert(sink_); + + if (mojo::internal::ControlMessageHandler::IsControlMessage(message)) { + if (!mojo::internal::ValidateControlResponse(message)) + return false; + return sink_->Accept(message); + } + + if (!mojo::internal::ValidateMessageIsResponse(message)) + return false; + switch (message->header()->name) { +{%- for method in interface.methods if method.response_parameters != None %} + case internal::k{{class_name}}_{{method.name}}_Name: { + if (!mojo::internal::ValidateMessagePayload< + internal::{{class_name}}_{{method.name}}_ResponseParams_Data>(message)) { + return false; + } + return sink_->Accept(message); + } +{%- endfor %} + default: + break; + } + + // Unrecognized message. + ReportValidationError( + mojo::internal::VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD); + return false; +} +{%- endif -%} diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl new file mode 100644 index 0000000..7644b2e --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl @@ -0,0 +1,23 @@ +{%- macro declare_params(prefix, parameters) %} +{%- for param in parameters -%} +{{param.kind|cpp_const_wrapper_type}} {{prefix}}{{param.name}} +{%- if not loop.last %}, {% endif %} +{%- endfor %} +{%- endmacro %} + +{%- macro declare_callback(method) -%} +mojo::Callback<void( +{%- for param in method.response_parameters -%} +{{param.kind|cpp_result_type}} +{%- if not loop.last %}, {% endif %} +{%- endfor -%} +)> +{%- endmacro -%} + +{%- macro declare_request_params(prefix, method) -%} +{{declare_params(prefix, method.parameters)}} +{%- if method.response_parameters != None -%} +{%- if method.parameters %}, {% endif -%} +const {{method.name}}Callback& callback +{%- endif -%} +{%- endmacro -%} diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl new file mode 100644 index 0000000..6ba1a16 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl @@ -0,0 +1,13 @@ +{%- import "interface_macros.tmpl" as interface_macros %} +class {{interface.name}}Proxy + : public {{interface.name}}, + public mojo::internal::ControlMessageProxy { + public: + explicit {{interface.name}}Proxy(mojo::MessageReceiverWithResponder* receiver); + +{%- for method in interface.methods %} + void {{method.name}}( + {{interface_macros.declare_request_params("", method)}} + ) override; +{%- endfor %} +}; diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_request_validator_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_request_validator_declaration.tmpl new file mode 100644 index 0000000..29917ea --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_request_validator_declaration.tmpl @@ -0,0 +1,6 @@ +class {{interface.name}}RequestValidator : public mojo::MessageFilter { + public: + explicit {{interface.name}}RequestValidator(mojo::MessageReceiver* sink = nullptr); + + bool Accept(mojo::Message* message) override; +}; diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_response_validator_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_response_validator_declaration.tmpl new file mode 100644 index 0000000..5893bfd --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_response_validator_declaration.tmpl @@ -0,0 +1,6 @@ +class {{interface.name}}ResponseValidator : public mojo::MessageFilter { + public: + explicit {{interface.name}}ResponseValidator(mojo::MessageReceiver* sink = nullptr); + + bool Accept(mojo::Message* message) override; +}; diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl new file mode 100644 index 0000000..c6afa75 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl @@ -0,0 +1,15 @@ +class {{interface.name}}Stub : public mojo::MessageReceiverWithResponderStatus { + public: + {{interface.name}}Stub(); + ~{{interface.name}}Stub() override; + void set_sink({{interface.name}}* sink) { sink_ = sink; } + {{interface.name}}* sink() { return sink_; } + + bool Accept(mojo::Message* message) override; + bool AcceptWithResponder(mojo::Message* message, + mojo::MessageReceiverWithStatus* responder) override; + + private: + {{interface.name}}* sink_; + mojo::internal::ControlMessageHandler control_message_handler_; +}; diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module-internal.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module-internal.h.tmpl new file mode 100644 index 0000000..2d748fc --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/module-internal.h.tmpl @@ -0,0 +1,72 @@ +// 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. + +{%- set header_guard = "%s_INTERNAL_H_"| + format(module.path|upper|replace("/","_")|replace(".","_")) %} + +#ifndef {{header_guard}} +#define {{header_guard}} + +#include "mojo/public/cpp/bindings/lib/bindings_internal.h" +#include "mojo/public/cpp/bindings/lib/buffer.h" +#include "mojo/public/cpp/bindings/lib/union_accessor.h" +#include "mojo/public/cpp/bindings/lib/value_traits.h" +#include "mojo/public/cpp/bindings/struct_ptr.h" + +{%- for import in imports %} +#include "{{import.module.path}}-internal.h" +{%- endfor %} + +namespace mojo { +namespace internal { +class BoundsChecker; +} +} + +{%- for namespace in namespaces_as_array %} +namespace {{namespace}} { +{%- endfor %} + +{#--- Wrapper forward declarations #} +{% for struct in structs %} +class {{struct.name}}; +{%- endfor %} + +{#--- Wrapper forward declarations for unions #} +{% for union in unions %} +class {{union.name}}; +{%- endfor %} + +namespace internal { + +{#--- Internal forward declarations #} +{% for struct in structs %} +class {{struct.name}}_Data; +{%- endfor %} + +{% for union in unions %} +class {{union.name}}_Data; +{%- endfor %} + +#pragma pack(push, 1) + +{#--- Unions must be declared first because they can be members of structs #} +{#--- Union class declarations #} +{% for union in unions %} +{% include "union_declaration.tmpl" %} +{%- endfor %} + +{#--- Class declarations #} +{% for struct in structs %} +{% include "struct_declaration.tmpl" %} +{%- endfor %} + +#pragma pack(pop) + +} // namespace internal +{%- for namespace in namespaces_as_array|reverse %} +} // namespace {{namespace}} +{%- endfor %} + +#endif // {{header_guard}} diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl new file mode 100644 index 0000000..dddb6ab --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl @@ -0,0 +1,132 @@ +// 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. + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-private-field" +#elif defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4056) +#pragma warning(disable:4065) +#pragma warning(disable:4756) +#endif + +#include "{{module.path}}.h" + +#include <math.h> + +#include "base/trace_event/trace_event.h" +#include "mojo/public/cpp/bindings/lib/array_serialization.h" +#include "mojo/public/cpp/bindings/lib/bindings_serialization.h" +#include "mojo/public/cpp/bindings/lib/bounds_checker.h" +#include "mojo/public/cpp/bindings/lib/map_data_internal.h" +#include "mojo/public/cpp/bindings/lib/map_serialization.h" +#include "mojo/public/cpp/bindings/lib/message_builder.h" +#include "mojo/public/cpp/bindings/lib/string_serialization.h" +#include "mojo/public/cpp/bindings/lib/validate_params.h" +#include "mojo/public/cpp/bindings/lib/validation_errors.h" +#include "mojo/public/cpp/bindings/lib/validation_util.h" +#include "mojo/public/cpp/environment/lib/scoped_task_tracking.h" +#include "mojo/public/cpp/environment/logging.h" +#include "mojo/public/interfaces/bindings/interface_control_messages.mojom.h" + +{%- for namespace in namespaces_as_array %} +namespace {{namespace}} { +{%- endfor %} + +{#--- Constants #} +{%- for constant in module.constants %} +{%- if not constant.kind|is_integral_kind %} +const {{constant.kind|cpp_pod_type}} {{constant.name}} = {{constant|constant_value}}; +{%- endif %} +{%- endfor %} + +namespace internal { +namespace { + +#pragma pack(push, 1) + +{#--- Interface parameter definitions #} +{%- for interface in interfaces %} +{%- for method in interface.methods %} +{%- set method_name = "k%s_%s_Name"|format(interface.name, method.name) %} +const uint32_t {{method_name}} = {{method.ordinal}}; +{% set struct = method.param_struct %} +{% include "struct_declaration.tmpl" %} +{%- include "struct_definition.tmpl" %} +{%- if method.response_parameters != None %} +{%- set struct = method.response_param_struct %} +{% include "struct_declaration.tmpl" %} +{%- include "struct_definition.tmpl" %} +{%- endif %} +{%- endfor %} +{%- endfor %} + +#pragma pack(pop) + +} // namespace + +{#--- Struct definitions #} +{% for struct in structs %} +{%- include "struct_definition.tmpl" %} +{%- endfor %} + +{#--- Union definitions #} +{% for union in unions %} +{%- include "union_definition.tmpl" %} +{%- endfor %} + +} // namespace internal + +{#--- Enums #} +{%- from "enum_macros.tmpl" import is_valid_enum_def -%} +{%- for enum in enums -%} + {{is_valid_enum_def(enum, class_name='')}} +{%- endfor %} + +{#--- Struct Constants #} +{%- for struct in structs %} +{%- for constant in struct.constants %} +{%- if constant.kind|is_integral_kind %} +MOJO_STATIC_CONST_MEMBER_DEFINITION const {{constant.kind|cpp_pod_type}} {{struct.name}}::{{constant.name}}; +{%- else %} +MOJO_STATIC_CONST_MEMBER_DEFINITION const {{constant.kind|cpp_pod_type}} {{struct.name}}::{{constant.name}} = {{constant|constant_value}}; +{%- endif %} +{%- endfor %} +{%- endfor %} + +{#--- Struct builder definitions #} +{%- for struct in structs %} +{%- include "wrapper_class_definition.tmpl" %} +{%- endfor %} + +{#--- Union builder definitions #} +{%- for union in unions %} +{%- include "wrapper_union_class_definition.tmpl" %} +{%- endfor %} + +{#--- Interface definitions #} +{%- for interface in interfaces %} +{%- include "interface_definition.tmpl" %} +{%- endfor %} + +{#--- Struct Serialization Helpers #} +{%- for struct in structs %} +{%- include "struct_serialization_definition.tmpl" %} +{%- endfor %} + +{#--- Union Serialization Helpers #} +{%- for union in unions %} +{%- include "union_serialization_definition.tmpl" %} +{%- endfor %} + +{%- for namespace in namespaces_as_array|reverse %} +} // namespace {{namespace}} +{%- endfor %} + +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(_MSC_VER) +#pragma warning(pop) +#endif diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl new file mode 100644 index 0000000..b6ba1a3 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl @@ -0,0 +1,144 @@ +// 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. + +{%- set header_guard = "%s_H_"| + format(module.path|upper|replace("/","_")|replace(".","_")) %} + +#ifndef {{header_guard}} +#define {{header_guard}} + +#include <stdint.h> + +#include "mojo/public/cpp/bindings/array.h" +#include "mojo/public/cpp/bindings/associated_interface_ptr_info.h" +#include "mojo/public/cpp/bindings/associated_interface_request.h" +#include "mojo/public/cpp/bindings/callback.h" +#include "mojo/public/cpp/bindings/interface_ptr.h" +#include "mojo/public/cpp/bindings/interface_request.h" +#include "mojo/public/cpp/bindings/lib/control_message_handler.h" +#include "mojo/public/cpp/bindings/lib/control_message_proxy.h" +#include "mojo/public/cpp/bindings/map.h" +#include "mojo/public/cpp/bindings/message_filter.h" +#include "mojo/public/cpp/bindings/no_interface.h" +#include "mojo/public/cpp/bindings/string.h" +#include "mojo/public/cpp/bindings/struct_ptr.h" +#include "{{module.path}}-internal.h" +{%- for import in imports %} +#include "{{import.module.path}}.h" +{%- endfor %} + +{%- for namespace in namespaces_as_array %} +namespace {{namespace}} { +{%- endfor %} + +{#--- Enums #} +{% from "enum_macros.tmpl" import enum_decl -%} +{% for enum in enums %} + {{enum_decl(enum)}} +{%- endfor %} + +{#--- Constants #} +{%- for constant in module.constants %} +{#- To be consistent with constants defined inside interfaces, only make + integral types compile-time constants. #} +{%- if constant.kind|is_integral_kind %} +const {{constant.kind|cpp_pod_type}} {{constant.name}} = {{constant|constant_value}}; +{%- else %} +extern const {{constant.kind|cpp_pod_type}} {{constant.name}}; +{%- endif %} +{%- endfor %} + +{#--- Interface Forward Declarations -#} +{% for interface in interfaces %} +class {{interface.name}}; +using {{interface.name}}Ptr = mojo::InterfacePtr<{{interface.name}}>; +{% endfor %} + +{#--- Struct Forward Declarations -#} +{% for struct in structs %} +class {{struct.name}}; +{% if struct|should_inline %} +using {{struct.name}}Ptr = mojo::InlinedStructPtr<{{struct.name}}>; +{% else %} +using {{struct.name}}Ptr = mojo::StructPtr<{{struct.name}}>; +{% endif %} +{% endfor %} + +{#--- Union Forward Declarations -#} +{% for union in unions %} +class {{union.name}}; +{% if union|should_inline_union %} +typedef mojo::InlinedStructPtr<{{union.name}}> {{union.name}}Ptr; +{% else %} +typedef mojo::StructPtr<{{union.name}}> {{union.name}}Ptr; +{% endif %} +{%- endfor %} + +{#--- Interfaces -#} +{% for interface in interfaces %} +{% include "interface_declaration.tmpl" %} +{%- endfor %} + +{#--- Interface Proxies -#} +{% for interface in interfaces %} +{% include "interface_proxy_declaration.tmpl" %} +{%- endfor %} + +{#--- Interface Stubs -#} +{% for interface in interfaces %} +{% include "interface_stub_declaration.tmpl" %} +{%- endfor %} + +{#--- Interface Request Validators -#} +{% for interface in interfaces %} +{% include "interface_request_validator_declaration.tmpl" %} +{%- endfor %} + +{#--- Interface Response Validators -#} +{% for interface in interfaces if interface|has_callbacks %} +{% include "interface_response_validator_declaration.tmpl" %} +{%- endfor %} + +{#--- Unions must be declared first because they can be members of structs #} +{#--- Unions #} +{% for union in unions %} +{% include "wrapper_union_class_declaration.tmpl" %} +{%- endfor %} + +{#--- NOTE: Non-inlined structs may have pointers to inlined structs, so we #} +{#--- need to fully define inlined structs ahead of the others. #} + +{#--- Inlined structs #} +{% for struct in structs %} +{% if struct|should_inline %} +{% include "wrapper_class_declaration.tmpl" %} +{% endif %} +{%- endfor %} + +{#--- Non-inlined structs #} +{% for struct in structs %} +{% if not struct|should_inline %} +{% include "wrapper_class_declaration.tmpl" %} +{% endif %} +{%- endfor %} + +{#--- Struct Serialization Helpers -#} +{% if structs %} +{% for struct in structs %} +{% include "struct_serialization_declaration.tmpl" %} +{%- endfor %} +{%- endif %} + +{#--- Union Serialization Helpers -#} +{% if unions %} +{% for union in unions %} +{% include "union_serialization_declaration.tmpl" %} +{%- endfor %} +{%- endif %} + +{%- for namespace in namespaces_as_array|reverse %} +} // namespace {{namespace}} +{%- endfor %} + +#endif // {{header_guard}} diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl new file mode 100644 index 0000000..255005a --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl @@ -0,0 +1,48 @@ +{%- set class_name = struct.name ~ "_Data" -%} + +class {{class_name}} { + public: + static {{class_name}}* New(mojo::internal::Buffer* buf); + + static bool Validate(const void* data, + mojo::internal::BoundsChecker* bounds_checker); + + void EncodePointersAndHandles(std::vector<mojo::Handle>* handles); + void DecodePointersAndHandles(std::vector<mojo::Handle>* handles); + + mojo::internal::StructHeader header_; +{%- for packed_field in struct.packed.packed_fields %} +{%- set name = packed_field.field.name %} +{%- set kind = packed_field.field.kind %} +{%- if kind.spec == 'b' %} + uint8_t {{name}} : 1; +{%- elif kind|is_enum_kind %} + int32_t {{name}}; +{%- else %} + {{kind|cpp_field_type}} {{name}}; +{%- endif %} +{%- if not loop.last %} +{%- set next_pf = struct.packed.packed_fields[loop.index0 + 1] %} +{%- set pad = next_pf.offset - (packed_field.offset + packed_field.size) %} +{%- if pad > 0 %} + uint8_t pad{{loop.index0}}_[{{pad}}]; +{%- endif %} +{%- endif %} +{%- endfor %} + +{%- set num_fields = struct.versions[-1].num_fields %} +{%- if num_fields > 0 %} +{%- set last_field = struct.packed.packed_fields[num_fields - 1] %} +{%- set offset = last_field.offset + last_field.size %} +{%- set pad = offset|get_pad(8) %} +{%- if pad > 0 %} + uint8_t padfinal_[{{pad}}]; +{%- endif %} +{%- endif %} + + private: + {{class_name}}(); + ~{{class_name}}() = delete; +}; +static_assert(sizeof({{class_name}}) == {{struct.versions[-1].num_bytes}}, + "Bad sizeof({{class_name}})"); diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl new file mode 100644 index 0000000..77f2ab0 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl @@ -0,0 +1,211 @@ +{%- set class_name = struct.name ~ "_Data" %} + +{#- TODO(yzshen): Consider eliminating _validate_object() and + _validate_handle(). #} + +{#- Validates the specified struct field, which is supposed to be an object + (struct/array/string/map/union). + This macro is expanded by the Validate() method. #} +{%- macro _validate_object(struct, packed_field) %} +{%- set name = packed_field.field.name %} +{%- set kind = packed_field.field.kind %} +{%- set wrapper_type = kind|cpp_wrapper_type %} +{%- if not kind|is_nullable_kind %} +{%- if kind|is_union_kind %} + if (object->{{name}}.is_null()) { +{%- else %} + if (!object->{{name}}.offset) { +{%- endif %} + ReportValidationError( + mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, + "null {{name}} field in {{struct.name}} struct"); + return false; + } +{%- endif %} +{%- if not kind|is_union_kind %} + if (!mojo::internal::ValidateEncodedPointer(&object->{{name}}.offset)) { + ReportValidationError(mojo::internal::VALIDATION_ERROR_ILLEGAL_POINTER); + return false; + } +{%- endif %} +{%- if kind|is_array_kind or kind|is_string_kind %} + const mojo::internal::ArrayValidateParams {{name}}_validate_params( + {{kind|get_array_validate_params_ctor_args|indent(6)}}); + if (!{{wrapper_type}}::Data_::Validate( + mojo::internal::DecodePointerRaw(&object->{{name}}.offset), + bounds_checker, &{{name}}_validate_params)) { +{%- elif kind|is_map_kind %} + const mojo::internal::ArrayValidateParams {{name}}_validate_params( + {{kind.value_kind|get_map_validate_params_ctor_args|indent(6)}}); + if (!{{wrapper_type}}::Data_::Validate( + mojo::internal::DecodePointerRaw(&object->{{name}}.offset), + bounds_checker, &{{name}}_validate_params)) { +{%- elif kind|is_struct_kind %} + if (!{{kind|get_name_for_kind}}::Data_::Validate( + mojo::internal::DecodePointerRaw(&object->{{name}}.offset), + bounds_checker)) { +{%- elif kind|is_union_kind %} + if (!{{kind|get_name_for_kind}}::Data_::Validate( + &object->{{name}}, bounds_checker, true)) { +{%- else %} + if (!{{wrapper_type}}::Data_::Validate( + mojo::internal::DecodePointerRaw(&object->{{name}}.offset), + bounds_checker)) { +{%- endif %} + return false; + } +{%- endmacro %} + +{#- Validates the specified struct field, which is supposed to be a handle or + contain a handle (in the case of interfaces). + This macro is expanded by the Validate() method. #} +{%- macro _validate_handle(struct, packed_field) %} +{%- set name = packed_field.field.name %} +{%- set kind = packed_field.field.kind %} +{%- if kind|is_interface_kind %} + const mojo::Handle {{name}}_handle = object->{{name}}.handle; +{%- else %} + const mojo::Handle {{name}}_handle = object->{{name}}; +{%- endif %} +{%- if not kind|is_nullable_kind %} + if ({{name}}_handle.value() == mojo::internal::kEncodedInvalidHandleValue) { + ReportValidationError( + mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE, + "invalid {{name}} field in {{struct.name}} struct"); + return false; + } +{%- endif %} + if (!bounds_checker->ClaimHandle({{name}}_handle)) { + ReportValidationError(mojo::internal::VALIDATION_ERROR_ILLEGAL_HANDLE); + return false; + } +{%- endmacro %} + +{#- Validates the specified struct field, which is supposed to be an associated + interface or an associated interface request. + This macro is expanded by the Validate() method. #} +{%- macro _validate_associated(struct, packed_field) %} + // TODO(yzshen): add validation for associated kinds. +{%- endmacro %} + +// static +{{class_name}}* {{class_name}}::New(mojo::internal::Buffer* buf) { + return new (buf->Allocate(sizeof({{class_name}}))) {{class_name}}(); +} + +// static +bool {{class_name}}::Validate(const void* data, + mojo::internal::BoundsChecker* bounds_checker) { + if (!data) + return true; + + if (!ValidateStructHeaderAndClaimMemory(data, bounds_checker)) + return false; + + // NOTE: The memory backing |object| may be smaller than |sizeof(*object)| if + // the message comes from an older version. + const {{class_name}}* object = static_cast<const {{class_name}}*>(data); + + static const struct { + uint32_t version; + uint32_t num_bytes; + } kVersionSizes[] = { +{%- for version in struct.versions -%} + { {{version.version}}, {{version.num_bytes}} }{% if not loop.last %}, {% endif -%} +{%- endfor -%} + }; + + if (object->header_.version <= + kVersionSizes[MOJO_ARRAYSIZE(kVersionSizes) - 1].version) { + // Scan in reverse order to optimize for more recent versions. + for (int i = MOJO_ARRAYSIZE(kVersionSizes) - 1; i >= 0; --i) { + if (object->header_.version >= kVersionSizes[i].version) { + if (object->header_.num_bytes == kVersionSizes[i].num_bytes) + break; + + ReportValidationError( + mojo::internal::VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER); + return false; + } + } + } else if (object->header_.num_bytes < + kVersionSizes[MOJO_ARRAYSIZE(kVersionSizes) - 1].num_bytes) { + ReportValidationError( + mojo::internal::VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER); + return false; + } + +{#- 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. #} +{%- set last_checked_version = 0 %} +{%- for packed_field in struct.packed.packed_fields_in_ordinal_order %} +{%- set kind = packed_field.field.kind %} +{%- if kind|is_object_kind or kind|is_any_handle_kind or + kind|is_interface_kind or kind|is_associated_kind %} +{%- if packed_field.min_version > last_checked_version %} +{%- set last_checked_version = packed_field.min_version %} + if (object->header_.version < {{packed_field.min_version}}) + return true; +{%- endif %} +{%- if kind|is_object_kind %} +{{_validate_object(struct, packed_field)}} +{%- elif kind|is_any_handle_kind or kind|is_interface_kind %} +{{_validate_handle(struct, packed_field)}} +{%- else %} +{{_validate_associated(struct, packed_field)}} +{%- endif %} +{%- endif %} +{%- endfor %} + + return true; +} + +void {{class_name}}::EncodePointersAndHandles( + std::vector<mojo::Handle>* handles) { + MOJO_CHECK(header_.version == {{struct.versions[-1].version}}); +{%- for pf in struct.packed.packed_fields_in_ordinal_order %} +{%- if pf.field.kind|is_union_kind %} + {{pf.field.name}}.EncodePointersAndHandles(handles); +{%- elif pf.field.kind|is_object_kind %} + mojo::internal::Encode(&{{pf.field.name}}, handles); +{%- elif pf.field.kind|is_any_handle_kind or pf.field.kind|is_interface_kind %} + mojo::internal::EncodeHandle(&{{pf.field.name}}, handles); +{%- endif %} +{%- endfor %} +} + +void {{class_name}}::DecodePointersAndHandles( + std::vector<mojo::Handle>* handles) { + // NOTE: The memory backing |this| may has be smaller than |sizeof(*this)|, if + // the message comes from an older version. +{#- Before decoding fields introduced at a certain version, we need to add + a version check, which makes sure we skip further decoding if |this| + is from an earlier version. |last_checked_version| records the last + version that we have added such version check. #} +{%- set last_checked_version = 0 %} +{%- for pf in struct.packed.packed_fields_in_ordinal_order %} +{%- set name = pf.field.name %} +{%- set kind = pf.field.kind %} +{%- if kind|is_object_kind or kind|is_any_handle_kind or kind|is_interface_kind %} +{%- if pf.min_version > last_checked_version %} +{%- set last_checked_version = pf.min_version %} + if (header_.version < {{pf.min_version}}) + return; +{%- endif %} +{%- if kind|is_union_kind %} + {{name}}.DecodePointersAndHandles(handles); +{%- elif kind|is_object_kind %} + mojo::internal::Decode(&{{name}}, handles); +{%- else %} + mojo::internal::DecodeHandle(&{{name}}, handles); +{%- endif %} +{%- endif %} +{%- endfor %} +} + +{{class_name}}::{{class_name}}() { + header_.num_bytes = sizeof(*this); + header_.version = {{struct.versions[-1].version}}; +} diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl new file mode 100644 index 0000000..234083a --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl @@ -0,0 +1,150 @@ +{# TODO(yzshen): Make these templates more readable. #} + +{# Computes the serialized size for the specified struct. + |struct| is the struct definition. + |input_field_pattern| should be a pattern that contains one string + placeholder, for example, "input->%s", "p_%s". The placeholder will be + substituted with struct field names to refer to the input fields. + This macro is expanded to compute seriailized size for both: + - user-defined structs: the input is an instance of the corresponding struct + wrapper class. + - method parameters/response parameters: the input is a list of + arguments. + It declares |size| of type size_t to store the resulting size. #} +{%- macro get_serialized_size(struct, input_field_pattern) -%} + size_t size = sizeof(internal::{{struct.name}}_Data); +{%- for pf in struct.packed.packed_fields_in_ordinal_order if pf.field.kind|is_object_kind %} +{%- if pf.field.kind|is_union_kind %} + size += GetSerializedSize_({{input_field_pattern|format(pf.field.name)}}, true); +{%- else %} + size += GetSerializedSize_({{input_field_pattern|format(pf.field.name)}}); +{%- endif %} +{%- endfor %} +{%- endmacro -%} + +{# Serializes the specified struct. + |struct| is the struct definition. + |struct_display_name| is the display name for the struct that can be showed + in error/log messages, for example, "FooStruct", "FooMethod request". + |input_field_pattern| should be a pattern that contains one string + placeholder, for example, "input->%s", "p_%s". The placeholder will be + substituted with struct field names to refer to the input fields. + |output| is the name of the output struct instance. + |buffer| is the name of the Buffer instance used. + This macro is expanded to do serialization for both: + - user-defined structs: the input is an instance of the corresponding struct + wrapper class. + - method parameters/response parameters: the input is a list of + arguments. #} +{%- macro serialize(struct, struct_display_name, input_field_pattern, output, buffer) -%} + internal::{{struct.name}}_Data* {{output}} = + internal::{{struct.name}}_Data::New({{buffer}}); +{%- for pf in struct.packed.packed_fields_in_ordinal_order %} +{%- set input_field = input_field_pattern|format(pf.field.name) %} +{%- set name = pf.field.name %} +{%- set kind = pf.field.kind %} +{%- if kind|is_object_kind %} +{%- if kind|is_array_kind %} + const mojo::internal::ArrayValidateParams {{name}}_validate_params( + {{kind|get_array_validate_params_ctor_args|indent(10)}}); + mojo::SerializeArray_(mojo::internal::Forward({{input_field}}), {{buffer}}, + &{{output}}->{{name}}.ptr, &{{name}}_validate_params); +{%- elif kind|is_map_kind %} + const mojo::internal::ArrayValidateParams {{name}}_validate_params( + {{kind.value_kind|get_map_validate_params_ctor_args|indent(10)}}); + mojo::SerializeMap_( + mojo::internal::Forward({{input_field}}), {{buffer}}, &{{output}}->{{name}}.ptr, + &{{name}}_validate_params); +{%- elif kind|is_union_kind %} + internal::{{kind.name}}_Data* {{name}}_ptr = &{{output}}->{{name}}; + SerializeUnion_(mojo::internal::Forward({{input_field}}), {{buffer}}, &{{name}}_ptr, true); +{%- else %} + Serialize_(mojo::internal::Forward({{input_field}}), {{buffer}}, &{{output}}->{{name}}.ptr); +{%- endif %} +{%- if not kind|is_nullable_kind %} + MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( +{%- if kind|is_union_kind %} + {{output}}->{{name}}.is_null(), +{%- else %} + !{{output}}->{{name}}.ptr, +{%- endif %} + mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, + "null {{name}} in {{struct_display_name}}"); +{%- endif %} +{%- elif kind|is_any_handle_kind or kind|is_interface_kind %} +{%- if kind|is_interface_kind %} + mojo::internal::InterfacePointerToData({{input_field}}.Pass(), &{{output}}->{{name}}); +{%- elif kind|is_interface_request_kind %} + {{output}}->{{name}} = {{input_field}}.PassMessagePipe().release(); +{%- else %} + {{output}}->{{name}} = {{input_field}}.release(); +{%- endif %} +{%- if not kind|is_nullable_kind %} + MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( +{%- if kind|is_interface_kind %} + !{{output}}->{{name}}.handle.is_valid(), +{%- else %} + !{{output}}->{{name}}.is_valid(), +{%- endif %} + mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE, + "invalid {{name}} in {{struct_display_name}}"); +{%- endif %} +{%- elif kind|is_associated_kind %} + // TODO(yzshen): add serialization logic for associated kinds. +{%- else %} + {{output}}->{{name}} = {{input_field}}; +{%- endif %} +{%- endfor %} +{%- endmacro -%} + +{# Deserializes the specified struct. + |struct| is the struct definition. + |input| is the name of the input struct instance. + |output_field_pattern| should be a pattern that contains one string + placeholder, for example, "result->%s", "p_%s". The placeholder will be + substituted with struct field names to refer to the output fields. + This macro is expanded to do deserialization for both: + - user-defined structs: the output is an instance of the corresponding + struct wrapper class. + - method parameters/response parameters: the output is a list of + arguments. #} +{%- macro deserialize(struct, input, output_field_pattern) -%} + do { + // NOTE: The memory backing |{{input}}| may has be smaller than + // |sizeof(*{{input}})| if the message comes from an older version. +{#- Before deserialize fields introduced at a certain version, we need to add + a version check, which makes sure we skip further deserialization if + |input| is from an earlier version. |last_checked_version| records the + last version that we have added such version check. #} +{%- set last_checked_version = 0 %} +{%- for pf in struct.packed.packed_fields_in_ordinal_order %} +{%- set output_field = output_field_pattern|format(pf.field.name) %} +{%- set name = pf.field.name %} +{%- set kind = pf.field.kind %} +{%- if pf.min_version > last_checked_version %} +{%- set last_checked_version = pf.min_version %} + if ({{input}}->header_.version < {{pf.min_version}}) + break; +{%- endif %} +{%- if kind|is_object_kind %} +{%- if kind|is_union_kind %} + Deserialize_(&{{input}}->{{name}}, &{{output_field}}); +{%- else %} + Deserialize_({{input}}->{{name}}.ptr, &{{output_field}}); +{%- endif %} +{%- elif kind|is_interface_kind %} + mojo::internal::InterfaceDataToPointer(&{{input}}->{{name}}, &{{output_field}}); +{%- elif kind|is_interface_request_kind %} + {{output_field}}.Bind(mojo::MakeScopedHandle(mojo::internal::FetchAndReset(&{{input}}->{{name}}))); +{%- elif kind|is_any_handle_kind %} + {{output_field}}.reset(mojo::internal::FetchAndReset(&{{input}}->{{name}})); +{%- elif kind|is_associated_kind %} + // TODO(yzshen): add deserialization logic for associated kinds. +{%- elif kind|is_enum_kind %} + {{output_field}} = static_cast<{{kind|cpp_wrapper_type}}>({{input}}->{{name}}); +{%- else %} + {{output_field}} = {{input}}->{{name}}; +{%- endif %} +{%- endfor %} + } while (false); +{%- endmacro %} diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl new file mode 100644 index 0000000..604be86 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl @@ -0,0 +1,5 @@ +size_t GetSerializedSize_(const {{struct.name}}Ptr& input); +void Serialize_({{struct.name}}Ptr input, mojo::internal::Buffer* buffer, + internal::{{struct.name}}_Data** output); +void Deserialize_(internal::{{struct.name}}_Data* input, + {{struct.name}}Ptr* output); diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl new file mode 100644 index 0000000..fe25553 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl @@ -0,0 +1,28 @@ +{%- import "struct_macros.tmpl" as struct_macros %} +size_t GetSerializedSize_(const {{struct.name}}Ptr& input) { + if (!input) + return 0; + {{struct_macros.get_serialized_size(struct, "input->%s")}} + return size; +} + +void Serialize_({{struct.name}}Ptr input, mojo::internal::Buffer* buf, + internal::{{struct.name}}_Data** output) { + if (input) { + {{struct_macros.serialize(struct, struct.name ~ " struct", "input->%s", "result", "buf")|indent(2)}} + *output = result; + } else { + *output = nullptr; + } +} + +void Deserialize_(internal::{{struct.name}}_Data* input, + {{struct.name}}Ptr* output) { + if (input) { + {{struct.name}}Ptr result({{struct.name}}::New()); + {{struct_macros.deserialize(struct, "input", "result->%s")|indent(2)}} + *output = result.Pass(); + } else { + output->reset(); + } +} diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_declaration.tmpl new file mode 100644 index 0000000..8c0e89c --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/union_declaration.tmpl @@ -0,0 +1,54 @@ +{%- set class_name = union.name ~ "_Data" -%} +{%- set enum_name = union.name ~ "_Tag" -%} +{%- import "struct_macros.tmpl" as struct_macros %} + +class {{class_name}} { + public: + // Used to identify Mojom Union Data Classes. + typedef void MojomUnionDataType; + static {{class_name}}* New(mojo::internal::Buffer* buf); + {{class_name}}(); + // Do nothing in the destructor since it won't be called. + ~{{class_name}}() {} + + static bool Validate(const void* data, + mojo::internal::BoundsChecker* bounds_checker, + bool inlined); + + bool is_null() const { + return size == 0; + } + + void set_null(); + + enum class {{enum_name}} : uint32_t { +{% for field in union.fields %} + {{field.name|upper}}, +{%- endfor %} + }; + + // A note on layout: + // "Each non-static data member is allocated as if it were the sole member of + // a struct." - Section 9.5.2 ISO/IEC 14882:2011 (The C++ Spec) + union MOJO_ALIGNAS(8) Union_ { +{%- for field in union.fields %} +{%- if field.kind.spec == 'b' %} + uint8_t f_{{field.name}} : 1; +{%- else %} + {{field.kind|cpp_union_field_type}} f_{{field.name}}; +{%- endif %} +{%- endfor %} + uint64_t unknown; + }; + + uint32_t size; + {{enum_name}} tag; + Union_ data; + + void EncodePointersAndHandles(std::vector<mojo::Handle>* handles); + void DecodePointersAndHandles(std::vector<mojo::Handle>* handles); +}; +static_assert(sizeof({{class_name}}) == 16, + "Bad sizeof({{class_name}})"); +static_assert(sizeof({{class_name}}::Union_) == 8, + "Bad sizeof({{class_name}}::Union_)"); diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_definition.tmpl new file mode 100644 index 0000000..087499a --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/union_definition.tmpl @@ -0,0 +1,96 @@ +{%- import "validation_macros.tmpl" as validation_macros %} +{%- set class_name = union.name ~ "_Data" %} +{%- set enum_name = union.name ~ "_Tag" -%} + +// static +{{class_name}}* {{class_name}}::New(mojo::internal::Buffer* buf) { + return new (buf->Allocate(sizeof({{class_name}}))) {{class_name}}(); +} + +// static +bool {{class_name}}::Validate(const void* data, + mojo::internal::BoundsChecker* bounds_checker, + bool inlined) { + if (!data) { + return true; + } + + if (!mojo::internal::IsAligned(data)) { + ReportValidationError(mojo::internal::VALIDATION_ERROR_MISALIGNED_OBJECT); + return false; + } + + // If the union is inlined in another structure its memory was already claimed. + // This ONLY applies to the union itself, NOT anything which the union points + // to. + if (!inlined && !bounds_checker->ClaimMemory(data, sizeof({{class_name}}))) { + ReportValidationError( + mojo::internal::VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE); + return false; + } + const {{class_name}}* object = static_cast<const {{class_name}}*>(data); + MOJO_ALLOW_UNUSED_LOCAL(object); + + switch (object->tag) { +{% for field in union.fields %} + case {{enum_name}}::{{field.name|upper}}: { +{{ validation_macros.validate_union_field(field, union)|indent(8) }} + } +{%- endfor %} + default: { + ReportValidationError( + mojo::internal::VALIDATION_ERROR_UNKNOWN_UNION_TAG, + "unknown tag in {{union.name}}"); + return false; + } + } +} + +void {{class_name}}::set_null() { + size = 0U; + tag = static_cast<{{enum_name}}>(0); + data.unknown = 0U; +} + +{{class_name}}::{{class_name}}() { +} + +void {{class_name}}::EncodePointersAndHandles( + std::vector<mojo::Handle>* handles) { + switch (tag) { +{%- for field in union.fields %} + case {{enum_name}}::{{field.name|upper}}: { +{%- if field.kind|is_object_kind %} + mojo::internal::Encode(&data.f_{{field.name}}, handles); +{%- elif field.kind|is_any_handle_kind %} + mojo::internal::EncodeHandle(&data.f_{{field.name}}, handles); +{%- elif field.kind|is_interface_kind %} + mojo::internal::EncodeHandle( + reinterpret_cast<mojo::internal::Interface_Data*>( + &data.f_{{field.name}}), handles); +{%- endif %} + return; + } +{%- endfor %} + } +} + +void {{class_name}}::DecodePointersAndHandles( + std::vector<mojo::Handle>* handles) { + switch (tag) { +{%- for field in union.fields %} + case {{enum_name}}::{{field.name|upper}}: { +{%- if field.kind|is_object_kind %} + mojo::internal::Decode(&data.f_{{field.name}}, handles); +{%- elif field.kind|is_any_handle_kind %} + mojo::internal::DecodeHandle(&data.f_{{field.name}}, handles); +{%- elif field.kind|is_interface_kind %} + mojo::internal::DecodeHandle( + reinterpret_cast<mojo::internal::Interface_Data*>( + &data.f_{{field.name}}), handles); +{%- endif %} + return; + } +{%- endfor %} + } +} diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_declaration.tmpl new file mode 100644 index 0000000..404eb83 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_declaration.tmpl @@ -0,0 +1,5 @@ +size_t GetSerializedSize_(const {{union.name}}Ptr& input, bool inlined); +void SerializeUnion_({{union.name}}Ptr input, mojo::internal::Buffer* buffer, + internal::{{union.name}}_Data** output, bool inlined); +void Deserialize_(internal::{{union.name}}_Data* input, + {{union.name}}Ptr* output); diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_definition.tmpl new file mode 100644 index 0000000..696de8e --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_definition.tmpl @@ -0,0 +1,134 @@ +size_t GetSerializedSize_(const {{union.name}}Ptr& input, bool inlined) { + size_t size = 0U; + if (!inlined) { + size += sizeof(internal::{{union.name}}_Data); + } + + if (!input) + return size; + + mojo::internal::UnionAccessor<{{union.name}}> input_acc(input.get()); + switch (input->which()) { +{% for field in union.fields %} +{% if field.kind|is_object_kind %} + case {{union.name}}::Tag::{{field.name|upper}}: +{% if field.kind|is_union_kind %} + size += GetSerializedSize_(*(input_acc.data()->{{field.name}}), false); +{% else %} + size += GetSerializedSize_(*(input_acc.data()->{{field.name}})); +{% endif %} + break; +{%- endif %} +{%- endfor %} + default: + break; + } + return size; +} + +void SerializeUnion_({{union.name}}Ptr input, mojo::internal::Buffer* buf, + internal::{{union.name}}_Data** output, bool inlined) { + internal::{{union.name}}_Data* result = *output; + if (input) { + if (!inlined) { + result = internal::{{union.name}}_Data::New(buf); + } + mojo::internal::UnionAccessor<{{union.name}}> input_acc(input.get()); + // TODO(azani): Handle unknown and objects. + // Set the not-null flag. + result->size = 16; + result->tag = input->which(); + switch (input->which()) { +{% for field in union.fields %} + case {{union.name}}::Tag::{{field.name|upper}}: { +{% if field.kind|is_object_kind %} +{% if field.kind|is_string_kind %} + Serialize_( + *(input_acc.data()->{{field.name}}), + buf, &result->data.f_{{field.name}}.ptr); +{% elif field.kind|is_struct_kind %} + Serialize_( + mojo::internal::Forward(*(input_acc.data()->{{field.name}})), + buf, &result->data.f_{{field.name}}.ptr); +{% elif field.kind|is_union_kind %} + SerializeUnion_( + mojo::internal::Forward(*(input_acc.data()->{{field.name}})), + buf, &result->data.f_{{field.name}}.ptr, false); +{% elif field.kind|is_array_kind %} + const mojo::internal::ArrayValidateParams {{field.name}}_validate_params( + {{field.kind|get_array_validate_params_ctor_args|indent(16)}}); + SerializeArray_( + mojo::internal::Forward(*(input_acc.data()->{{field.name}})), + buf, &result->data.f_{{field.name}}.ptr, &{{field.name}}_validate_params); +{% elif field.kind|is_map_kind %} + const mojo::internal::ArrayValidateParams {{field.name}}_validate_params( + {{field.kind.value_kind|get_map_validate_params_ctor_args|indent(16)}}); + SerializeMap_( + mojo::internal::Forward(*(input_acc.data()->{{field.name}})), + buf, &result->data.f_{{field.name}}.ptr, &{{field.name}}_validate_params); +{%- endif %} +{% elif field.kind|is_any_handle_kind %} + result->data.f_{{field.name}} = + input_acc.data()->{{field.name}}->release().value(); +{% elif field.kind|is_interface_kind %} + mojo::internal::Interface_Data* {{field.name}} = + reinterpret_cast<mojo::internal::Interface_Data*>( + &result->data.f_{{field.name}}); + mojo::internal::InterfacePointerToData( + input_acc.data()->{{field.name}}->Pass(), {{field.name}}); +{%- elif field.kind|is_associated_kind %} + // TODO(yzshen): add seralization logic for associated kinds. +{% else %} + result->data.f_{{field.name}} = input_acc.data()->{{field.name}}; +{%- endif %} + break; + } +{%- endfor %} + } + } else if (inlined) { + result->set_null(); + } else { + result = nullptr; + } + *output = result; +} + +void Deserialize_(internal::{{union.name}}_Data* input, + {{union.name}}Ptr* output) { + if (input && !input->is_null()) { + {{union.name}}Ptr result({{union.name}}::New()); + mojo::internal::UnionAccessor<{{union.name}}> result_acc(result.get()); + switch (input->tag) { +{% for field in union.fields %} + case {{union.name}}::Tag::{{field.name|upper}}: { +{% if field.kind|is_object_kind %} + result_acc.SwitchActive({{union.name}}::Tag::{{field.name|upper}}); + Deserialize_(input->data.f_{{field.name}}.ptr, result_acc.data()->{{field.name}}); +{% elif field.kind|is_any_handle_kind %} + {{field.kind|cpp_wrapper_type}}* {{field.name}} = + reinterpret_cast<{{field.kind|cpp_wrapper_type}}*>(&input->data.f_{{field.name}}); + result->set_{{field.name}}({{field.name}}->Pass()); +{% elif field.kind|is_interface_kind %} + {{field.kind|cpp_wrapper_type}} {{field.name}}_out; + mojo::internal::Interface_Data* {{field.name}}_in = + reinterpret_cast<mojo::internal::Interface_Data*>( + &input->data.f_{{field.name}}); + mojo::internal::InterfaceDataToPointer( + {{field.name}}_in, &{{field.name}}_out); + result->set_{{field.name}}({{field.name}}_out.Pass()); +{%- elif field.kind|is_associated_kind %} + // TODO(yzshen): add deserialization logic for associated kinds. +{% elif field.kind|is_enum_kind %} + result->set_{{field.name}}(static_cast<{{field.kind|cpp_wrapper_type}}>(input->data.f_{{field.name}})); +{% else %} + result->set_{{field.name}}(input->data.f_{{field.name}}); +{%- endif %} + break; + } +{%- endfor %} + } + *output = result.Pass(); + } else { + output->reset(); + } +} diff --git a/mojo/public/tools/bindings/generators/cpp_templates/validation_macros.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/validation_macros.tmpl new file mode 100644 index 0000000..5cb1dc8 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/validation_macros.tmpl @@ -0,0 +1,65 @@ +{%- macro validate_not_null_ptr(field_expr, field, object_name) %} +if (!{{field_expr}}->offset) { + ReportValidationError( + mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, + "null {{field.name}} field in {{object_name}}"); + return false; +} +{%- endmacro %} + +{%- macro validate_encoded_ptr(field_expr) %} +if (!mojo::internal::ValidateEncodedPointer(&{{field_expr}}->offset)) { + ReportValidationError(mojo::internal::VALIDATION_ERROR_ILLEGAL_POINTER); + return false; +} +{%- endmacro %} + +{%- macro validate_array_or_string(field_expr, field) -%} +const mojo::internal::ArrayValidateParams {{field.name}}_validate_params( + {{field.kind|get_array_validate_params_ctor_args|indent(4)}}); +if (!{{field.kind|cpp_wrapper_type}}::Data_::Validate( + mojo::internal::DecodePointerRaw(&{{field_expr}}->offset), + bounds_checker, &{{field.name}}_validate_params)) { + return false; +} +{%- endmacro %} + +{%- macro validate_handle(field_expr, field, object_name) -%} + const mojo::Handle {{field.name}}_handle(object->data.f_{{field.name}}); + +{%- if not field.kind|is_nullable_kind %} + if ({{field.name}}_handle.value() == mojo::internal::kEncodedInvalidHandleValue) { + ReportValidationError( + mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE, + "invalid {{field.name}} field in {{object_name}}"); + return false; + } +{%- endif %} + if (!bounds_checker->ClaimHandle({{field.name}}_handle)) { + ReportValidationError(mojo::internal::VALIDATION_ERROR_ILLEGAL_HANDLE); + return false; + } +{%- endmacro -%} + +{%- macro validate_union_field(field, union) %} +{%- set field_expr = "(reinterpret_cast<const " + ~ field.kind|cpp_union_field_type + ~ "*>(&object->data.f_" + ~ field.name + ~ "))" -%} +{%- if field.kind|is_object_kind -%} +{%- if not field.kind|is_nullable_kind -%} +{{ validate_not_null_ptr(field_expr, field, union.name) }} +{%- endif %} +{{ validate_encoded_ptr(field_expr) }} +{%- endif %} + +{%- if field.kind|is_array_kind or field.kind|is_string_kind -%} +{{ validate_array_or_string(field_expr, field) }} +{%- endif %} + +{%- if field.kind|is_any_handle_kind -%} +{{ validate_handle(field_expr, field, union.name) }} +{%- endif %} +return true; +{%- endmacro %} diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl new file mode 100644 index 0000000..0293acc --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl @@ -0,0 +1,46 @@ +{% from "enum_macros.tmpl" import enum_decl -%} +class {{struct.name}} { + public: + using Data_ = internal::{{struct.name}}_Data; + +{#--- Enums #} +{%- for enum in struct.enums -%} + {{enum_decl(enum, is_static=true)|indent(2)}} +{%- endfor %} + +{#--- Constants #} +{%- for constant in struct.constants %} +{%- if constant.kind|is_integral_kind %} + static const {{constant.kind|cpp_pod_type}} {{constant.name}} = {{constant|constant_value}}; +{%- else %} + static const {{constant.kind|cpp_pod_type}} {{constant.name}}; +{%- endif %} +{%- endfor %} + + static {{struct.name}}Ptr New(); + + template <typename U> + static {{struct.name}}Ptr From(const U& u) { + return mojo::TypeConverter<{{struct.name}}Ptr, U>::Convert(u); + } + + template <typename U> + U To() const { + return mojo::TypeConverter<U, {{struct.name}}>::Convert(*this); + } + + {{struct.name}}(); + ~{{struct.name}}(); + +{% if struct|is_cloneable_kind %} + {{struct.name}}Ptr Clone() const; +{%- endif %} + bool Equals(const {{struct.name}}& other) const; + +{#--- Struct members #} +{% for field in struct.fields %} +{%- set type = field.kind|cpp_wrapper_type %} +{%- set name = field.name %} + {{type}} {{name}}; +{%- endfor %} +}; diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_definition.tmpl new file mode 100644 index 0000000..8c61c86 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_definition.tmpl @@ -0,0 +1,44 @@ +{%- from "enum_macros.tmpl" import is_valid_enum_def %} + +{#--- Enums #} +{%- for enum in struct.enums -%} + {{is_valid_enum_def(enum, class_name=struct.name)}} +{%- endfor %} + +// static +{{struct.name}}Ptr {{struct.name}}::New() { + {{struct.name}}Ptr rv; + mojo::internal::StructHelper<{{struct.name}}>::Initialize(&rv); + return rv.Pass(); +} + +{{struct.name}}::{{struct.name}}() +{%- for field in struct.fields %} + {% if loop.first %}:{% else %} {% endif %} {{field.name}}({{field|default_value}}){% if not loop.last %},{% endif %} +{%- endfor %} { +} + +{{struct.name}}::~{{struct.name}}() { +} + +{% if struct|is_cloneable_kind %} +{{struct.name}}Ptr {{struct.name}}::Clone() const { + {{struct.name}}Ptr rv(New()); +{%- for field in struct.fields %} +{%- if field.kind|is_object_kind and not field.kind|is_string_kind %} + rv->{{field.name}} = {{field.name}}.Clone(); +{%- else %} + rv->{{field.name}} = {{field.name}}; +{%- endif %} +{%- endfor %} + return rv.Pass(); +} +{% endif %} + +bool {{struct.name}}::Equals(const {{struct.name}}& other) const { +{%- for field in struct.fields %} + if (!mojo::internal::ValueTraits<{{field.kind|cpp_wrapper_type}}>::Equals(this->{{field.name}}, other.{{field.name}})) + return false; +{%- endfor %} + return true; +} diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_declaration.tmpl new file mode 100644 index 0000000..04a60f6 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_declaration.tmpl @@ -0,0 +1,55 @@ +class {{union.name}} { + public: + using Data_ = internal::{{union.name}}_Data; + using Tag = Data_::{{union.name}}_Tag; + + static {{union.name}}Ptr New(); + + template <typename U> + static {{union.name}}Ptr From(const U& u) { + return mojo::TypeConverter<{{union.name}}Ptr, U>::Convert(u); + } + + template <typename U> + U To() const { + return mojo::TypeConverter<U, {{union.name}}>::Convert(*this); + } + + {{union.name}}(); + ~{{union.name}}(); + +{% if union|is_cloneable_kind %} + {{union.name}}Ptr Clone() const; +{%- endif %} + bool Equals(const {{union.name}}& other) const; + + Tag which() const { + return tag_; + } + +{% for field in union.fields %} + bool is_{{field.name}}() const; + {{field.kind|cpp_union_getter_return_type}} get_{{field.name}}() const; + void set_{{field.name}}({{field.kind|cpp_const_wrapper_type}} {{field.name}}); +{%- endfor %} + + private: + friend class mojo::internal::UnionAccessor<{{union.name}}>; + union Union_ { + Union_() {} + ~Union_() {} +{% for field in union.fields %} +{% if field.kind|is_object_kind or field.kind|is_any_handle_kind or + field.kind|is_interface_kind or field.kind|is_associated_kind -%} + {{field.kind|cpp_wrapper_type}}* {{field.name}}; +{% else -%} + {{field.kind|cpp_wrapper_type}} {{field.name}}; +{% endif -%} +{%- endfor %} + }; + void SwitchActive(Tag new_active); + void SetActive(Tag new_active); + void DestroyActive(); + Tag tag_; + Union_ data_; +}; diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_definition.tmpl new file mode 100644 index 0000000..0dbaf8b --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_definition.tmpl @@ -0,0 +1,123 @@ +// static +{{union.name}}Ptr {{union.name}}::New() { + {{union.name}}Ptr rv; + mojo::internal::StructHelper<{{union.name}}>::Initialize(&rv); + return rv.Pass(); +} + +{{union.name}}::{{union.name}}() { + // TODO(azani): Implement default values here when/if we support them. + // TODO(azani): Set to UNKNOWN when unknown is implemented. + SetActive(static_cast<Tag>(0)); +} + +{{union.name}}::~{{union.name}}() { + DestroyActive(); +} + +{% if union|is_cloneable_kind %} +{{union.name}}Ptr {{union.name}}::Clone() const { + {{union.name}}Ptr rv(New()); + switch (tag_) { +{% for field in union.fields %} + case Tag::{{field.name|upper}}: +{% if field.kind|is_string_kind %} + rv->set_{{field.name}}(*(data_.{{field.name}})); +{% elif field.kind|is_object_kind %} + rv->set_{{field.name}}(data_.{{field.name}}->Clone()); +{%- else %} + rv->set_{{field.name}}(data_.{{field.name}}); +{%- endif %} + break; +{%- endfor %} + }; + return rv.Pass(); +} +{%- endif %} + +bool {{union.name}}::Equals(const {{union.name}}& other) const { + if (tag_ != other.which()) { + return false; + } + + switch (tag_) { +{% for field in union.fields %} + case Tag::{{field.name|upper}}: +{% if field.kind|is_object_kind or field.kind|is_any_handle_kind or + field.kind|is_interface_kind or field.kind|is_associated_kind %} + return mojo::internal::ValueTraits<{{field.kind|cpp_wrapper_type}}>::Equals(*(data_.{{field.name}}), *(other.data_.{{field.name}})); +{%- else %} + return mojo::internal::ValueTraits<{{field.kind|cpp_wrapper_type}}>::Equals(data_.{{field.name}}, other.data_.{{field.name}}); +{%- endif %} +{%- endfor %} + }; + + return false; +} + +{% for field in union.fields %} +bool {{union.name}}::is_{{field.name}}() const { + return tag_ == Tag::{{field.name|upper}}; +} + +{{field.kind|cpp_union_getter_return_type}} {{union.name}}::get_{{field.name}}() const { + MOJO_DCHECK(tag_ == Tag::{{field.name|upper}}); +{% if field.kind|is_object_kind or field.kind|is_any_handle_kind or + field.kind|is_interface_kind or field.kind|is_associated_kind %} + return *(data_.{{field.name}}); +{%- else %} + return data_.{{field.name}}; +{%- endif %} +} + +void {{union.name}}::set_{{field.name}}({{field.kind|cpp_const_wrapper_type}} {{field.name}}) { + SwitchActive(Tag::{{field.name|upper}}); +{% if field.kind|is_string_kind %} + *(data_.{{field.name}}) = {{field.name}}; +{% elif field.kind|is_object_kind or field.kind|is_interface_kind or + field.kind|is_associated_kind %} + *(data_.{{field.name}}) = {{field.name}}.Pass(); +{% elif field.kind|is_any_handle_kind %} + data_.{{field.name}}->reset({{field.name}}.release()); +{%- else %} + data_.{{field.name}} = {{field.name}}; +{%- endif %} +} +{%- endfor %} + +void {{union.name}}::SwitchActive(Tag new_active) { + if (new_active == tag_) { + return; + } + + DestroyActive(); + SetActive(new_active); +} + +void {{union.name}}::SetActive(Tag new_active) { + switch (new_active) { +{% for field in union.fields %} + case Tag::{{field.name|upper}}: +{% if field.kind|is_object_kind or field.kind|is_any_handle_kind or + field.kind|is_interface_kind or field.kind|is_associated_kind %} + data_.{{field.name}} = new {{field.kind|cpp_wrapper_type}}(); +{%- endif %} + break; +{%- endfor %} + } + + tag_ = new_active; +} + +void {{union.name}}::DestroyActive() { + switch (tag_) { +{% for field in union.fields %} + case Tag::{{field.name|upper}}: +{% if field.kind|is_object_kind or field.kind|is_any_handle_kind or + field.kind|is_interface_kind or field.kind|is_associated_kind %} + delete data_.{{field.name}}; +{%- endif %} + break; +{%- endfor %} + } +} diff --git a/mojo/public/tools/bindings/generators/java_templates/constant_definition.tmpl b/mojo/public/tools/bindings/generators/java_templates/constant_definition.tmpl new file mode 100644 index 0000000..db193e2 --- /dev/null +++ b/mojo/public/tools/bindings/generators/java_templates/constant_definition.tmpl @@ -0,0 +1,3 @@ +{% macro constant_def(constant) %} +public static final {{constant.kind|java_type}} {{constant|name}} = {{constant|constant_value}}; +{% endmacro %} diff --git a/mojo/public/tools/bindings/generators/java_templates/constants.java.tmpl b/mojo/public/tools/bindings/generators/java_templates/constants.java.tmpl new file mode 100644 index 0000000..0a4e299 --- /dev/null +++ b/mojo/public/tools/bindings/generators/java_templates/constants.java.tmpl @@ -0,0 +1,12 @@ +{% from "constant_definition.tmpl" import constant_def %} +{% include "header.java.tmpl" %} + +public final class {{main_entity}} { +{% for constant in constants %} + + {{constant_def(constant)|indent(4)}} +{% endfor %} + + private {{main_entity}}() {} + +} diff --git a/mojo/public/tools/bindings/generators/java_templates/data_types_definition.tmpl b/mojo/public/tools/bindings/generators/java_templates/data_types_definition.tmpl new file mode 100644 index 0000000..e9c3b65 --- /dev/null +++ b/mojo/public/tools/bindings/generators/java_templates/data_types_definition.tmpl @@ -0,0 +1,367 @@ +{% 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}}) +{%- else -%} +{% if ne %}!{% endif %}java.util.Arrays.equals({{v1}}, {{v2}}) +{%- endif -%} +{%- else -%} +{% if ne %}!{% endif %}org.chromium.mojo.bindings.BindingsHelper.equals({{v1}}, {{v2}}) +{%- endif -%} +{%- else -%} +{{v1}} {% if ne %}!={% else %}=={% endif %} {{v2}} +{%- endif -%} +{%- endmacro -%} + +{%- macro hash(kind, v) -%} +{%- if kind|is_array_kind -%} +{%- if kind.kind|is_reference_kind -%} +java.util.Arrays.deepHashCode({{v}}) +{%- else -%} +java.util.Arrays.hashCode({{v}}) +{%- endif -%} +{%- else -%} +org.chromium.mojo.bindings.BindingsHelper.hashCode({{v}}) +{%- endif -%} +{%- endmacro -%} + +{%- macro array_element_size(kind) -%} +{%- if kind|is_union_kind %} +org.chromium.mojo.bindings.BindingsHelper.UNION_SIZE +{%- else -%} +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 %} +if ({{variable}} == null) { + encoder{{level}}.encodeNullPointer({{offset}}, {{kind|is_nullable_kind|java_true_false}}); +} else { +{% else %} +{ +{% 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 %} +if ({{variable}} == null) { + encoder{{level}}.encodeNullPointer({{offset}}, {{kind|is_nullable_kind|java_true_false}}); +} else { + org.chromium.mojo.bindings.Encoder encoder{{level + 1}} = encoder{{level}}.encoderForMap({{offset}}); + int size{{level}} = {{variable}}.size(); + {{kind.key_kind|java_type}}[] keys{{level}} = {{kind.key_kind|array|new_array('size'~level)}}; + {{kind.value_kind|java_type}}[] values{{level}} = {{kind.value_kind|array|new_array('size'~level)}}; + int index{{level}} = 0; + for (java.util.Map.Entry<{{kind.key_kind|java_type(true)}}, {{kind.value_kind|java_type(true)}}> entry{{level}} : {{variable}}.entrySet()) { + keys{{level}}[index{{level}}] = entry{{level}}.getKey(); + values{{level}}[index{{level}}] = entry{{level}}.getValue(); + ++index{{level}}; + } + {{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 %} +encoder{{level}}.{{kind|encode_method(variable, offset, bit)}}; +{% endif %} +{% endmacro %} + +{% 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 %} +{{variable}} = {{kind|java_type}}.decode(decoder{{level+1}}); +{% else %}{# kind|is_pointer_array_kind or is_map_kind #} +{% if kind|is_nullable_kind %} +if (decoder{{level+1}} == null) { + {{variable}} = null; +} else { +{% else %} +{ +{% endif %} +{% if kind|is_map_kind %} + decoder{{level+1}}.readDataHeaderForMap(); + {{kind.key_kind|java_type}}[] keys{{level}}; + {{kind.value_kind|java_type}}[] values{{level}}; + { + {{decode('keys'~level, kind.key_kind|array, 'org.chromium.mojo.bindings.DataHeader.HEADER_SIZE', 0, level+1)|indent(8)}} + } + { + {{decode('values'~level, kind.value_kind|array('keys'~level~'.length'), 'org.chromium.mojo.bindings.DataHeader.HEADER_SIZE + org.chromium.mojo.bindings.BindingsHelper.POINTER_SIZE', 0, level+1)|indent(8)}} + } + {{variable}} = new java.util.HashMap<{{kind.key_kind|java_type(true)}}, {{kind.value_kind|java_type(true)}}>(); + for (int index{{level}} = 0; index{{level}} < keys{{level}}.length; ++index{{level}}) { + {{variable}}.put(keys{{level}}[index{{level}}], values{{level}}[index{{level}}]); + } +{% 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 %} +{% elif kind|is_union_kind %} +{{variable}} = {{kind|java_type}}.decode(decoder{{level}}, {{offset}}); +{% else %} +{{variable}} = decoder{{level}}.{{kind|decode_method(offset, bit)}}; +{% endif %} +{% endmacro %} + +{% 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 -%} +{%- endfor -%} + }; + private static final org.chromium.mojo.bindings.DataHeader DEFAULT_STRUCT_INFO = VERSION_ARRAY[{{struct.versions|length - 1}}]; +{% for constant in struct.constants %} + + {{constant_def(constant)|indent(4)}} +{% endfor %} +{% for enum in struct.enums %} + + {{enum_def(enum, false)|indent(4)}} +{% endfor %} +{% if struct.fields %} + +{% for field in struct.fields %} + public {{field.kind|java_type}} {{field|name}}; +{% endfor %} +{% endif %} + + private {{struct|name}}(int version) { + super(STRUCT_SIZE, version); +{% for field in struct.fields %} +{% if field.default %} + {{field|name}} = {{field|default_value}}; +{% elif field.kind|is_any_handle_kind and not field.kind|is_interface_request_kind %} + {{field|name}} = org.chromium.mojo.system.InvalidHandle.INSTANCE; +{% endif %} +{% endfor %} + } + + public {{struct|name}}() { + this({{struct.versions[-1].version}}); + } + + public static {{struct|name}} deserialize(org.chromium.mojo.bindings.Message message) { + return decode(new org.chromium.mojo.bindings.Decoder(message)); + } + + @SuppressWarnings("unchecked") + public static {{struct|name}} decode(org.chromium.mojo.bindings.Decoder decoder0) { + 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)}} + } +{% endfor %} +{% endfor %} + return result; + } + + @SuppressWarnings("unchecked") + @Override + protected final void encode(org.chromium.mojo.bindings.Encoder encoder) { +{% if not struct.bytes %} + encoder.getEncoderAtDataOffset(DEFAULT_STRUCT_INFO); +{% 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 %} + {{encode(packed_field.field|name, packed_field.field.kind, 8+packed_field.offset, packed_field.bit)|indent(8)}} +{% endfor %} +{% endfor %} + } + + /** + * @see Object#equals(Object) + */ + @Override + public boolean equals(Object object) { + if (object == this) + return true; + if (object == null) + return false; + if (getClass() != object.getClass()) + return false; +{% if struct.fields|length %} + {{struct|name}} other = ({{struct|name}}) object; +{% for field in struct.fields %} + if ({{equality(field.kind, 'this.'~field|name, 'other.'~field|name, True)}}) + return false; +{% endfor %} +{% endif %} + return true; + } + + /** + * @see Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = prime + getClass().hashCode(); +{% for field in struct.fields %} + result = prime * result + {{hash(field.kind, field|name)}}; +{% endfor %} + return result; + } +} +{% endmacro %} + + +{% 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 %} + public static final int {{field|ucc}} = {{field.ordinal}}; +{% endfor %} + }; + + private int mTag_ = -1; +{% for field in union.fields %} + private {{field.kind|java_type}} m{{field|ucc}}; +{% endfor %} + + public int which() { + return mTag_; + } + + public boolean isUnknown() { + return mTag_ == -1; + } +{% for field in union.fields %} + + public void set{{field|ucc}}({{field.kind|java_type}} {{field|name}}) { + mTag_ = {{field.ordinal}}; + m{{field|ucc}} = {{field|name}}; + } + + public {{field.kind|java_type}} get{{field|ucc}}() { + assert mTag_ == {{field.ordinal}}; + return m{{field|ucc}}; + } +{% endfor %} + + + @Override + protected final void encode(org.chromium.mojo.bindings.Encoder encoder0, int offset) { + encoder0.encode(org.chromium.mojo.bindings.BindingsHelper.UNION_SIZE, offset); + encoder0.encode(mTag_, offset + 4); + switch (mTag_) { +{% for field in union.fields %} + case {{field.ordinal}}: { +{% 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 %} + {{encode('m' ~ field|ucc, field.kind, 'offset + 8', 0)|indent(16)}} +{% endif %} + break; + } +{% endfor %} + } + } + + public static {{union|name}} deserialize(org.chromium.mojo.bindings.Message message) { + return decode(new org.chromium.mojo.bindings.Decoder(message).decoderForSerializedUnion(), 0); + } + + public static final {{union|name}} decode(org.chromium.mojo.bindings.Decoder decoder0, int offset) { + org.chromium.mojo.bindings.DataHeader dataHeader = decoder0.readDataHeaderForUnion(offset); + if (dataHeader.size == 0) { + return null; + } + {{union|name}} result = new {{union|name}}(); + switch (dataHeader.elementsOrVersion) { +{% for field in union.fields %} + case {{field.ordinal}}: { +{% 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 %} + {{decode('result.m'~field|ucc, field.kind, 'offset + org.chromium.mojo.bindings.DataHeader.HEADER_SIZE', 0)|indent(16)}} +{% endif %} + result.mTag_ = {{field.ordinal}}; + break; + } +{% endfor %} + } + return result; + } + + /** + * @see Object#equals(Object) + */ + @Override + public boolean equals(Object object) { + if (object == this) + return true; + if (object == null) + return false; + if (getClass() != object.getClass()) + return false; + {{union|name}} other = ({{union|name}}) object; + if (mTag_ != other.mTag_) + return false; + switch (mTag_) { +{% for field in union.fields %} + case {{field.ordinal}}: + return {{equality(field.kind, 'm'~field|ucc, 'other.m'~field|ucc)}}; +{% endfor %} + } + return false; + } + + /** + * @see Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = prime + getClass().hashCode(); + result = prime * result + org.chromium.mojo.bindings.BindingsHelper.hashCode(mTag_); + switch (mTag_) { +{% for field in union.fields %} + case {{field.ordinal}}: + result = prime * result + {{hash(field.kind, 'm'~field|ucc)}}; +{% endfor %} + } + return result; + } +} +{% endmacro %} diff --git a/mojo/public/tools/bindings/generators/java_templates/enum.java.tmpl b/mojo/public/tools/bindings/generators/java_templates/enum.java.tmpl new file mode 100644 index 0000000..7096a18 --- /dev/null +++ b/mojo/public/tools/bindings/generators/java_templates/enum.java.tmpl @@ -0,0 +1,4 @@ +{% from "enum_definition.tmpl" import enum_def %} +{% include "header.java.tmpl" %} + +{{enum_def(enum, true)}} diff --git a/mojo/public/tools/bindings/generators/java_templates/enum_definition.tmpl b/mojo/public/tools/bindings/generators/java_templates/enum_definition.tmpl new file mode 100644 index 0000000..a16c178 --- /dev/null +++ b/mojo/public/tools/bindings/generators/java_templates/enum_definition.tmpl @@ -0,0 +1,21 @@ +{%- macro enum_value(enum, field, index) -%} +{%- if field.value -%} +(int) ({{field.value|expression_to_text('i32')}}) +{%- elif index == 0 -%} +0 +{%- else -%} +{{enum.fields[index - 1]|name}} + 1 +{%- endif -%} +{%- endmacro -%} + +{%- macro enum_def(enum, top_level) -%} +public {{ 'static ' if not top_level }}final class {{enum|name}} { + +{% for field in enum.fields %} + public static final int {{field|name}} = {{enum_value(enum, field, loop.index0)}}; +{% endfor %} + + private {{enum|name}}() {} + +} +{%- endmacro -%} diff --git a/mojo/public/tools/bindings/generators/java_templates/header.java.tmpl b/mojo/public/tools/bindings/generators/java_templates/header.java.tmpl new file mode 100644 index 0000000..ec6a88b --- /dev/null +++ b/mojo/public/tools/bindings/generators/java_templates/header.java.tmpl @@ -0,0 +1,11 @@ +// 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. + +// This file is autogenerated by: +// mojo/public/tools/bindings/mojom_bindings_generator.py +// For: +// {{module.path}} +// + +package {{package}}; diff --git a/mojo/public/tools/bindings/generators/java_templates/interface.java.tmpl b/mojo/public/tools/bindings/generators/java_templates/interface.java.tmpl new file mode 100644 index 0000000..a13be3e --- /dev/null +++ b/mojo/public/tools/bindings/generators/java_templates/interface.java.tmpl @@ -0,0 +1,4 @@ +{% from "interface_definition.tmpl" import interface_def %} +{% include "header.java.tmpl" %} + +{{ interface_def(interface) }} diff --git a/mojo/public/tools/bindings/generators/java_templates/interface_definition.tmpl b/mojo/public/tools/bindings/generators/java_templates/interface_definition.tmpl new file mode 100644 index 0000000..527e15f --- /dev/null +++ b/mojo/public/tools/bindings/generators/java_templates/interface_definition.tmpl @@ -0,0 +1,287 @@ +{% from "constant_definition.tmpl" import constant_def %} +{% from "enum_definition.tmpl" import enum_def %} +{% from "data_types_definition.tmpl" import struct_def %} + +{%- macro declare_params(parameters, boxed=false) %} +{%- for param in parameters -%} +{{param.kind|java_type(boxed)}} {{param|name}} +{%- if not loop.last %}, {% endif %} +{%- endfor %} +{%- endmacro %} + +{% macro declare_request_params(method) %} +{{declare_params(method.parameters)}} +{%- if method.response_parameters != None -%} +{%- if method.parameters %}, {% endif %} +{{method|interface_response_name}} callback +{%- endif -%} +{% endmacro %} + +{%- macro declare_callback(method) -%} + +interface {{method|interface_response_name}} extends org.chromium.mojo.bindings.Callbacks.Callback{{method.response_parameters|length}}{% if method.response_parameters %}< +{%- for param in method.response_parameters -%} +{{param.kind|java_type(True)}} +{%- if not loop.last %}, {% endif %} +{%- endfor -%} +>{% endif %} { } +{%- endmacro -%} + +{%- macro run_callback(variable, parameters) -%} +{%- if parameters -%} +{%- for param in parameters -%} +{{variable}}.{{param|name}} +{%- if not loop.last %}, {% endif %} +{%- endfor -%} +{%- endif -%} +{%- endmacro -%} + +{%- macro flags_for_method(method, is_request) -%} +{{flags(method.response_parameters != None, is_request)}} +{%- endmacro -%} + +{%- macro flags(use_response_flag, is_request) -%} +{%- if not use_response_flag -%} +org.chromium.mojo.bindings.MessageHeader.NO_FLAG +{%- elif is_request: -%} +org.chromium.mojo.bindings.MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG +{%- else -%} +org.chromium.mojo.bindings.MessageHeader.MESSAGE_IS_RESPONSE_FLAG +{%- endif -%} +{%- endmacro -%} + +{%- macro manager_class(interface, fully_qualified=False) -%} +{% if fully_qualified %}org.chromium.mojo.bindings.Interface.{% endif %}Manager<{{interface|name}}, {{interface|name}}.Proxy> +{%- endmacro -%} + +{%- macro manager_def(interface) -%} +public static final {{manager_class(interface, True)}} MANAGER = + new {{manager_class(interface, True)}}() { + + public String getName() { + return "{{namespace|replace(".","::")}}::{{interface.name}}"; + } + + public int getVersion() { + return {{interface.version}}; + } + + public Proxy buildProxy(org.chromium.mojo.system.Core core, + org.chromium.mojo.bindings.MessageReceiverWithResponder messageReceiver) { + return new Proxy(core, messageReceiver); + } + + public Stub buildStub(org.chromium.mojo.system.Core core, {{interface|name}} impl) { + return new Stub(core, impl); + } + + public {{interface|name}}[] buildArray(int size) { + return new {{interface|name}}[size]; + } +}; +{%- endmacro -%} + +{%- macro accept_body(interface, with_response) -%} +try { + org.chromium.mojo.bindings.ServiceMessage messageWithHeader = + message.asServiceMessage(); + org.chromium.mojo.bindings.MessageHeader header = messageWithHeader.getHeader(); + if (!header.validateHeader({{flags(with_response, True)}})) { + return false; + } + switch(header.getType()) { +{% if with_response %} + case org.chromium.mojo.bindings.InterfaceControlMessagesConstants.RUN_MESSAGE_ID: + return org.chromium.mojo.bindings.InterfaceControlMessagesHelper.handleRun( + getCore(), {{interface|name}}_Internal.MANAGER, messageWithHeader, receiver); +{% else %} + case org.chromium.mojo.bindings.InterfaceControlMessagesConstants.RUN_OR_CLOSE_PIPE_MESSAGE_ID: + return org.chromium.mojo.bindings.InterfaceControlMessagesHelper.handleRunOrClosePipe( + {{interface|name}}_Internal.MANAGER, messageWithHeader); +{% endif %} +{% for method in interface.methods %} +{% if (with_response and method.response_parameters != None) or + (not with_response and method.response_parameters == None) %} +{% set request_struct = method.param_struct %} +{% if with_response %} +{% set response_struct = method.response_param_struct %} +{% endif %} + case {{method|method_ordinal_name}}: { +{% if method.parameters %} + {{request_struct|name}} data = + {{request_struct|name}}.deserialize(messageWithHeader.getPayload()); +{% else %} + {{request_struct|name}}.deserialize(messageWithHeader.getPayload()); +{% endif %} + getImpl().{{method|name}}({{run_callback('data', method.parameters)}}{% if with_response %}{% if method.parameters %}, {% endif %}new {{response_struct|name}}ProxyToResponder(getCore(), receiver, header.getRequestId()){% endif %}); + return true; + } +{% endif %} +{% endfor %} + default: + return false; + } +} catch (org.chromium.mojo.bindings.DeserializationException e) { + System.err.println(e.toString()); + return false; +} +{%- endmacro -%} + +{% macro interface_def(interface) %} +public interface {{interface|name}} extends org.chromium.mojo.bindings.Interface { +{% for constant in interface.constants %} + + {{constant_def(constant)|indent(4)}} +{% endfor %} +{% for enum in interface.enums %} + + {{enum_def(enum, false)|indent(4)}} +{% endfor %} + + public interface Proxy extends {{interface|name}}, org.chromium.mojo.bindings.Interface.Proxy { + } + + {{manager_class(interface)}} MANAGER = {{interface|name}}_Internal.MANAGER; +{% for method in interface.methods %} + + void {{method|name}}({{declare_request_params(method)}}); +{% if method.response_parameters != None %} + {{declare_callback(method)|indent(4)}} +{% endif %} +{% endfor %} +} +{% endmacro %} + +{% macro interface_internal_def(interface) %} +class {{interface|name}}_Internal { + + {{manager_def(interface)|indent(4)}} + +{% for method in interface.methods %} + private static final int {{method|method_ordinal_name}} = {{method.ordinal}}; +{% endfor %} + + static final class Proxy extends org.chromium.mojo.bindings.Interface.AbstractProxy implements {{interface|name}}.Proxy { + + Proxy(org.chromium.mojo.system.Core core, + org.chromium.mojo.bindings.MessageReceiverWithResponder messageReceiver) { + super(core, messageReceiver); + } +{% for method in interface.methods %} + + @Override + public void {{method|name}}({{declare_request_params(method)}}) { +{% set request_struct = method.param_struct %} + {{request_struct|name}} _message = new {{request_struct|name}}(); +{% for param in method.parameters %} + _message.{{param|name}} = {{param|name}}; +{% endfor %} +{% if method.response_parameters != None %} + getProxyHandler().getMessageReceiver().acceptWithResponder( + _message.serializeWithHeader( + getProxyHandler().getCore(), + new org.chromium.mojo.bindings.MessageHeader( + {{method|method_ordinal_name}}, + {{flags_for_method(method, True)}}, + 0)), + new {{method.response_param_struct|name}}ForwardToCallback(callback)); +{% else %} + getProxyHandler().getMessageReceiver().accept( + _message.serializeWithHeader( + getProxyHandler().getCore(), + new org.chromium.mojo.bindings.MessageHeader({{method|method_ordinal_name}}))); +{% endif %} + } +{% endfor %} + + } + + static final class Stub extends org.chromium.mojo.bindings.Interface.Stub<{{interface|name}}> { + + Stub(org.chromium.mojo.system.Core core, {{interface|name}} impl) { + super(core, impl); + } + + @Override + public boolean accept(org.chromium.mojo.bindings.Message message) { + {{accept_body(interface, False)|indent(12)}} + } + + @Override + public boolean acceptWithResponder(org.chromium.mojo.bindings.Message message, org.chromium.mojo.bindings.MessageReceiver receiver) { + {{accept_body(interface, True)|indent(12)}} + } + } +{% for method in interface.methods %} + + {{ struct_def(method.param_struct, True)|indent(4) }} +{% if method.response_parameters != None %} +{% set response_struct = method.response_param_struct %} + + {{ struct_def(response_struct, True)|indent(4) }} + + static class {{response_struct|name}}ForwardToCallback extends org.chromium.mojo.bindings.SideEffectFreeCloseable + implements org.chromium.mojo.bindings.MessageReceiver { + private final {{interface|name}}.{{method|interface_response_name}} mCallback; + + {{response_struct|name}}ForwardToCallback({{interface|name}}.{{method|interface_response_name}} callback) { + this.mCallback = callback; + } + + @Override + public boolean accept(org.chromium.mojo.bindings.Message message) { + try { + org.chromium.mojo.bindings.ServiceMessage messageWithHeader = + message.asServiceMessage(); + org.chromium.mojo.bindings.MessageHeader header = messageWithHeader.getHeader(); + if (!header.validateHeader({{method|method_ordinal_name}}, + {{flags_for_method(method, False)}})) { + return false; + } +{% if method.response_parameters|length %} + {{response_struct|name}} response = {{response_struct|name}}.deserialize(messageWithHeader.getPayload()); +{% endif %} + mCallback.call({{run_callback('response', method.response_parameters)}}); + return true; + } catch (org.chromium.mojo.bindings.DeserializationException e) { + return false; + } + } + } + + static class {{response_struct|name}}ProxyToResponder implements {{interface|name}}.{{method|interface_response_name}} { + + private final org.chromium.mojo.system.Core mCore; + private final org.chromium.mojo.bindings.MessageReceiver mMessageReceiver; + private final long mRequestId; + + {{response_struct|name}}ProxyToResponder( + org.chromium.mojo.system.Core core, + org.chromium.mojo.bindings.MessageReceiver messageReceiver, + long requestId) { + mCore = core; + mMessageReceiver = messageReceiver; + mRequestId = requestId; + } + + @Override + public void call({{declare_params(method.response_parameters, true)}}) { + {{response_struct|name}} _response = new {{response_struct|name}}(); +{% for param in method.response_parameters %} + _response.{{param|name}} = {{param|name}}; +{% endfor %} + org.chromium.mojo.bindings.ServiceMessage _message = + _response.serializeWithHeader( + mCore, + new org.chromium.mojo.bindings.MessageHeader( + {{method|method_ordinal_name}}, + {{flags_for_method(method, False)}}, + mRequestId)); + mMessageReceiver.accept(_message); + } + } +{% endif %} +{% endfor %} + +} +{% endmacro %} diff --git a/mojo/public/tools/bindings/generators/java_templates/interface_internal.java.tmpl b/mojo/public/tools/bindings/generators/java_templates/interface_internal.java.tmpl new file mode 100644 index 0000000..50c7a7b --- /dev/null +++ b/mojo/public/tools/bindings/generators/java_templates/interface_internal.java.tmpl @@ -0,0 +1,4 @@ +{% from "interface_definition.tmpl" import interface_internal_def %} +{% include "header.java.tmpl" %} + +{{ interface_internal_def(interface) }} diff --git a/mojo/public/tools/bindings/generators/java_templates/struct.java.tmpl b/mojo/public/tools/bindings/generators/java_templates/struct.java.tmpl new file mode 100644 index 0000000..e28ba19 --- /dev/null +++ b/mojo/public/tools/bindings/generators/java_templates/struct.java.tmpl @@ -0,0 +1,4 @@ +{% from "data_types_definition.tmpl" import struct_def %} +{% include "header.java.tmpl" %} + +{{ struct_def(struct) }} diff --git a/mojo/public/tools/bindings/generators/java_templates/union.java.tmpl b/mojo/public/tools/bindings/generators/java_templates/union.java.tmpl new file mode 100644 index 0000000..b8cd4aa --- /dev/null +++ b/mojo/public/tools/bindings/generators/java_templates/union.java.tmpl @@ -0,0 +1,4 @@ +{% from "data_types_definition.tmpl" import union_def %} +{% include "header.java.tmpl" %} + +{{ union_def(union) }} diff --git a/mojo/public/tools/bindings/generators/js_templates/enum_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/enum_definition.tmpl new file mode 100644 index 0000000..4ae0a9b --- /dev/null +++ b/mojo/public/tools/bindings/generators/js_templates/enum_definition.tmpl @@ -0,0 +1,13 @@ +{%- macro enum_def(enum_name, enum) -%} + {{enum_name}} = {}; +{%- set prev_enum = 0 %} +{%- for field in enum.fields %} +{%- if field.value %} + {{enum_name}}.{{field.name}} = {{field.value|expression_to_text}}; +{%- elif loop.first %} + {{enum_name}}.{{field.name}} = 0; +{%- else %} + {{enum_name}}.{{field.name}} = {{enum_name}}.{{enum.fields[loop.index0 - 1].name}} + 1; +{%- endif %} +{%- endfor %} +{%- endmacro %} diff --git a/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl new file mode 100644 index 0000000..1b5cafa --- /dev/null +++ b/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl @@ -0,0 +1,188 @@ +{%- for method in interface.methods %} + var k{{interface.name}}_{{method.name}}_Name = {{method.ordinal}}; +{%- endfor %} + + function {{interface.name}}Proxy(receiver) { + bindings.ProxyBase.call(this, receiver); + } + {{interface.name}}Proxy.prototype = Object.create(bindings.ProxyBase.prototype); + +{%- for method in interface.methods %} + {{interface.name}}Proxy.prototype.{{method.name|stylize_method}} = function( +{%- for parameter in method.parameters -%} +{{parameter.name}}{% if not loop.last %}, {% endif %} +{%- endfor -%} +) { + var params = new {{interface.name}}_{{method.name}}_Params(); +{%- for parameter in method.parameters %} + params.{{parameter.name}} = {{parameter|js_proxy_method_parameter_value}}; +{%- endfor %} + +{%- if method.response_parameters == None %} + var builder = new codec.MessageBuilder( + k{{interface.name}}_{{method.name}}_Name, + codec.align({{interface.name}}_{{method.name}}_Params.encodedSize)); + builder.encodeStruct({{interface.name}}_{{method.name}}_Params, params); + var message = builder.finish(); + this.receiver_.accept(message); +{%- else %} + return new Promise(function(resolve, reject) { + var builder = new codec.MessageWithRequestIDBuilder( + k{{interface.name}}_{{method.name}}_Name, + codec.align({{interface.name}}_{{method.name}}_Params.encodedSize), + codec.kMessageExpectsResponse, 0); + builder.encodeStruct({{interface.name}}_{{method.name}}_Params, params); + var message = builder.finish(); + this.receiver_.acceptAndExpectResponse(message).then(function(message) { + var reader = new codec.MessageReader(message); + var responseParams = + reader.decodeStruct({{interface.name}}_{{method.name}}_ResponseParams); + resolve(responseParams); + }).catch(function(result) { + reject(Error("Connection error: " + result)); + }); + }.bind(this)); +{%- endif %} + }; +{%- endfor %} + + function {{interface.name}}Stub(delegate) { + bindings.StubBase.call(this, delegate); + } + {{interface.name}}Stub.prototype = Object.create(bindings.StubBase.prototype); + +{%- for method in interface.methods %} +{%- set js_method_name = method.name|stylize_method %} +{%- set delegate_expr = "bindings.StubBindings(this).delegate" %} + {{interface.name}}Stub.prototype.{{js_method_name}} = function({{method.parameters|map(attribute='name')|join(', ')}}) { + return {{delegate_expr}} && {{delegate_expr}}.{{js_method_name}} && {{delegate_expr}}.{{js_method_name}}({{method.parameters|map('js_stub_method_parameter_value')|join(', ')}}); + } +{%- endfor %} + + {{interface.name}}Stub.prototype.accept = function(message) { + var reader = new codec.MessageReader(message); + switch (reader.messageName) { +{%- for method in interface.methods %} +{%- if method.response_parameters == None %} + case k{{interface.name}}_{{method.name}}_Name: + var params = reader.decodeStruct({{interface.name}}_{{method.name}}_Params); + this.{{method.name|stylize_method}}( +{%- for parameter in method.parameters -%} + params.{{parameter.name}}{% if not loop.last %}, {% endif %} +{%- endfor %}); + return true; +{%- endif %} +{%- endfor %} + default: + return false; + } + }; + + {{interface.name}}Stub.prototype.acceptWithResponder = + function(message, responder) { + var reader = new codec.MessageReader(message); + switch (reader.messageName) { +{%- for method in interface.methods %} +{%- if method.response_parameters != None %} + case k{{interface.name}}_{{method.name}}_Name: + var params = reader.decodeStruct({{interface.name}}_{{method.name}}_Params); + return this.{{method.name|stylize_method}}( +{%- for parameter in method.parameters -%} +params.{{parameter.name}}{% if not loop.last %}, {% endif -%} +{%- endfor %}).then(function(response) { + var responseParams = + new {{interface.name}}_{{method.name}}_ResponseParams(); +{%- for parameter in method.response_parameters %} + responseParams.{{parameter.name}} = response.{{parameter.name}}; +{%- endfor %} + var builder = new codec.MessageWithRequestIDBuilder( + k{{interface.name}}_{{method.name}}_Name, + codec.align({{interface.name}}_{{method.name}}_ResponseParams.encodedSize), + codec.kMessageIsResponse, reader.requestID); + builder.encodeStruct({{interface.name}}_{{method.name}}_ResponseParams, + responseParams); + var message = builder.finish(); + responder.accept(message); + }); +{%- endif %} +{%- endfor %} + default: + return Promise.reject(Error("Unhandled message: " + reader.messageName)); + } + }; + +{#--- Validation #} + + function validate{{interface.name}}Request(messageValidator) { +{%- if not(interface.methods) %} + return validator.validationError.NONE; +{%- else %} + var message = messageValidator.message; + var paramsClass = null; + switch (message.getName()) { +{%- for method in interface.methods %} + case k{{interface.name}}_{{method.name}}_Name: +{%- if method.response_parameters == None %} + if (!message.expectsResponse() && !message.isResponse()) + paramsClass = {{interface.name}}_{{method.name}}_Params; +{%- else %} + if (message.expectsResponse()) + paramsClass = {{interface.name}}_{{method.name}}_Params; +{%- endif %} + break; +{%- endfor %} + } + if (paramsClass === null) + return validator.validationError.NONE; + return paramsClass.validate(messageValidator, messageValidator.message.getHeaderNumBytes()); +{%- endif %} + } + + function validate{{interface.name}}Response(messageValidator) { +{%- if not(interface|has_callbacks) %} + return validator.validationError.NONE; +{%- else %} + var message = messageValidator.message; + var paramsClass = null; + switch (message.getName()) { +{%- for method in interface.methods %} +{%- if method.response_parameters != None %} + case k{{interface.name}}_{{method.name}}_Name: + if (message.isResponse()) + paramsClass = {{interface.name}}_{{method.name}}_ResponseParams; + break; +{%- endif %} +{%- endfor %} + } + if (paramsClass === null) + return validator.validationError.NONE; + return paramsClass.validate(messageValidator, messageValidator.message.getHeaderNumBytes()); +{%- endif %} + } + + var {{interface.name}} = { + name: '{{namespace|replace(".","::")}}::{{interface.name}}', + proxyClass: {{interface.name}}Proxy, + stubClass: {{interface.name}}Stub, + validateRequest: validate{{interface.name}}Request, +{%- if interface|has_callbacks %} + validateResponse: validate{{interface.name}}Response, +{%- else %} + validateResponse: null, +{%- endif %} + }; +{#--- Interface Constants #} +{%- for constant in interface.constants %} + {{interface.name}}.{{constant.name}} = {{constant.value|expression_to_text}}, +{%- endfor %} +{#--- Interface Enums #} +{%- from "enum_definition.tmpl" import enum_def -%} +{%- for enum in interface.enums %} + {{ enum_def("%s.%s"|format(interface.name, enum.name), enum) }} +{%- endfor %} + {{interface.name}}Stub.prototype.validator = validate{{interface.name}}Request; +{%- if interface|has_callbacks %} + {{interface.name}}Proxy.prototype.validator = validate{{interface.name}}Response; +{%- else %} + {{interface.name}}Proxy.prototype.validator = null; +{%- endif %} diff --git a/mojo/public/tools/bindings/generators/js_templates/module.amd.tmpl b/mojo/public/tools/bindings/generators/js_templates/module.amd.tmpl new file mode 100644 index 0000000..6d7a1a2 --- /dev/null +++ b/mojo/public/tools/bindings/generators/js_templates/module.amd.tmpl @@ -0,0 +1,23 @@ +// 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. + +define("{{module.path}}", [ + "mojo/public/js/bindings", + "mojo/public/js/codec", + "mojo/public/js/connection", + "mojo/public/js/core", + "mojo/public/js/validator", +{%- for import in imports %} + "{{import.module.path}}", +{%- endfor %} +], function(bindings, codec, connection, core, validator +{%- for import in imports -%} + , {{import.unique_name}} +{%- endfor -%} +) { + +{%- include "module_definition.tmpl" %} + + return exports; +}); diff --git a/mojo/public/tools/bindings/generators/js_templates/module_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/module_definition.tmpl new file mode 100644 index 0000000..86ea2ce --- /dev/null +++ b/mojo/public/tools/bindings/generators/js_templates/module_definition.tmpl @@ -0,0 +1,49 @@ +{#--- Constants #} +{%- for constant in module.constants %} + var {{constant.name}} = {{constant.value|expression_to_text}}; +{%- endfor %} + +{#--- Enums #} +{%- from "enum_definition.tmpl" import enum_def %} +{%- for enum in enums %} + var {{ enum_def(enum.name, enum) }} +{%- endfor %} + +{#--- Struct definitions #} +{% for struct in structs %} +{%- include "struct_definition.tmpl" %} +{%- endfor -%} + +{#--- Union definitions #} +{%- from "union_definition.tmpl" import union_def %} +{%- for union in unions %} +{{union_def(union)|indent(2)}} +{%- endfor %} + +{#--- Interface definitions #} +{%- for interface in interfaces -%} +{%- include "interface_definition.tmpl" %} +{%- endfor %} + + var exports = {}; +{%- for constant in module.constants %} + exports.{{constant.name}} = {{constant.name}}; +{%- endfor %} +{%- for enum in enums %} + exports.{{enum.name}} = {{enum.name}}; +{%- endfor %} +{%- for struct in structs if struct.exported %} + exports.{{struct.name}} = {{struct.name}}; +{%- endfor %} +{%- for union in unions %} + exports.{{union.name}} = {{union.name}}; +{%- endfor %} +{%- for interface in interfaces %} + exports.{{interface.name}} = {{interface.name}}; +{#--- Interface Client #} +{%- if interface.client in interfaces|map(attribute='name') %} + exports.{{interface.name}}.client = {{interface.client}}; +{%- elif interface.client in imported_interfaces %} + exports.{{interface.name}}.client = {{imported_interfaces[interface.client]}}; +{%- endif %} +{%- endfor %} diff --git a/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl new file mode 100644 index 0000000..ca80d67 --- /dev/null +++ b/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl @@ -0,0 +1,100 @@ +{#--- Begin #} + function {{struct.name}}(values) { + this.initDefaults_(); + this.initFields_(values); + } + +{#--- Enums #} +{%- from "enum_definition.tmpl" import enum_def %} +{% for enum in struct.enums %} + {{enum_def("%s.%s"|format(struct.name, enum.name), enum)}} +{%- endfor %} + +{#--- Constants #} +{% for constant in struct.constants %} + {{struct.name}}.{{constant.name}} = {{constant.value|expression_to_text}}; +{%- endfor %} + +{#--- initDefaults() #} + {{struct.name}}.prototype.initDefaults_ = function() { +{%- for packed_field in struct.packed.packed_fields %} + this.{{packed_field.field.name}} = {{packed_field.field|default_value}}; +{%- endfor %} + }; + +{#--- initFields() #} + {{struct.name}}.prototype.initFields_ = function(fields) { + for(var field in fields) { + if (this.hasOwnProperty(field)) + this[field] = fields[field]; + } + }; + +{#--- Validation #} + + {{struct.name}}.validate = function(messageValidator, offset) { + var err; + err = messageValidator.validateStructHeader(offset, {{struct.name}}.encodedSize, {{struct.versions[-1].version}}); + if (err !== validator.validationError.NONE) + return err; + +{%- from "validation_macros.tmpl" import validate_struct_field %} +{%- for packed_field in struct.packed.packed_fields %} +{%- set offset = packed_field|field_offset %} +{%- set field = packed_field.field %} +{%- set name = struct.name ~ '.' ~ field.name %} +{{validate_struct_field(field, offset, name)|indent(4)}} +{%- endfor %} + + return validator.validationError.NONE; + }; + +{#--- Encoding and decoding #} + + {{struct.name}}.encodedSize = codec.kStructHeaderSize + {{struct.packed|payload_size}}; + + {{struct.name}}.decode = function(decoder) { + var packed; + var val = new {{struct.name}}(); + var numberOfBytes = decoder.readUint32(); + var version = decoder.readUint32(); +{%- for byte in struct.bytes %} +{%- if byte.packed_fields|length > 1 %} + packed = decoder.readUint8(); +{%- for packed_field in byte.packed_fields %} + val.{{packed_field.field.name}} = (packed >> {{packed_field.bit}}) & 1 ? true : false; +{%- endfor %} +{%- else %} +{%- for packed_field in byte.packed_fields %} + val.{{packed_field.field.name}} = decoder.{{packed_field.field.kind|decode_snippet}}; +{%- endfor %} +{%- endif %} +{%- if byte.is_padding %} + decoder.skip(1); +{%- endif %} +{%- endfor %} + return val; + }; + + {{struct.name}}.encode = function(encoder, val) { + var packed; + encoder.writeUint32({{struct.name}}.encodedSize); + encoder.writeUint32({{struct.versions[-1].version}}); + +{%- for byte in struct.bytes %} +{%- if byte.packed_fields|length > 1 %} + packed = 0; +{%- for packed_field in byte.packed_fields %} + packed |= (val.{{packed_field.field.name}} & 1) << {{packed_field.bit}} +{%- endfor %} + encoder.writeUint8(packed); +{%- else %} +{%- for packed_field in byte.packed_fields %} + encoder.{{packed_field.field.kind|encode_snippet}}val.{{packed_field.field.name}}); +{%- endfor %} +{%- endif %} +{%- if byte.is_padding %} + encoder.skip(1); +{%- endif %} +{%- endfor %} + }; diff --git a/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl new file mode 100644 index 0000000..3c903bc --- /dev/null +++ b/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl @@ -0,0 +1,146 @@ +{%- macro union_def(union) %} +function {{union.name}}(value) { + this.initDefault_(); + this.initValue_(value); +} + +{{tags(union)}} + +{{union.name}}.prototype.initDefault_ = function() { + this.$data = null; + this.$tag = undefined; +} + +{{union.name}}.prototype.initValue_ = function(value) { + if (value == undefined) { + return; + } + + var keys = Object.keys(value); + if (keys.length == 0) { + return; + } + + if (keys.length > 1) { + throw new TypeError("You may set only one member on a union."); + } + + var fields = [ +{%- for field in union.fields %} + "{{field.name}}", +{%- endfor %} + ]; + + if (fields.indexOf(keys[0]) < 0) { + throw new ReferenceError(keys[0] + " is not a {{union.name}} member."); + + } + + this[keys[0]] = value[keys[0]]; +} + +{%- for field in union.fields %} +Object.defineProperty({{union.name}}.prototype, "{{field.name}}", { + get: function() { + if (this.$tag != {{union.name}}.Tags.{{field.name}}) { + throw new ReferenceError( + "{{union.name}}.{{field.name}} is not currently set."); + } + return this.$data; + }, + + set: function(value) { + this.$tag = {{union.name}}.Tags.{{field.name}}; + this.$data = value; + } +}); +{%- endfor %} + +{{encode(union)|indent(2)}} + +{{decode(union)|indent(2)}} + +{{validate(union)|indent(2)}} + +{{union.name}}.encodedSize = 16; +{%- endmacro %} + +{%- macro tags(union) %} +{{union.name}}.Tags = { +{%- for field in union.fields %} + {{field.name}}: {{field.ordinal}}, +{%- endfor %} +}; +{%- endmacro %} + +{%- macro encode(union) %} +{{union.name}}.encode = function(encoder, val) { + if (val == null) { + encoder.writeUint64(0); + encoder.writeUint64(0); + return; + } + if (val.$tag == undefined) { + throw new TypeError("Cannot encode unions with an unknown member set."); + } + + encoder.writeUint32(16); + encoder.writeUint32(val.$tag); + switch (val.$tag) { +{%- for field in union.fields %} + case {{union.name}}.Tags.{{field.name}}: + encoder.{{field.kind|union_encode_snippet}}val.{{field.name}}); + break; +{%- endfor %} + } + encoder.align(); +}; +{%- endmacro %} + +{%- macro decode(union) %} +{{union.name}}.decode = function(decoder) { + var size = decoder.readUint32(); + if (size == 0) { + decoder.readUint32(); + decoder.readUint64(); + return null; + } + + var result = new {{union.name}}(); + var tag = decoder.readUint32(); + switch (tag) { +{%- for field in union.fields %} + case {{union.name}}.Tags.{{field.name}}: + result.{{field.name}} = decoder.{{field.kind|union_decode_snippet}}; + break; +{%- endfor %} + } + decoder.align(); + + return result; +}; +{%- endmacro %} + +{%- from "validation_macros.tmpl" import validate_union_field %} +{%- macro validate(union) %} +{{union.name}}.validate = function(messageValidator, offset) { + var size = messageValidator.decodeUnionSize(offset); + if (size != 16) { + return validator.validationError.INVALID_UNION_SIZE; + } + + var tag = messageValidator.decodeUnionTag(offset); + var data_offset = offset + 8; + var err; + switch (tag) { +{%- for field in union.fields %} +{%- set name = union.name ~ '.' ~ field.name %} + case {{union.name}}.Tags.{{field.name}}: + {{validate_union_field(field, "data_offset", name)}} + break; +{%- endfor %} + } + + return validator.validationError.NONE; +}; +{%- endmacro %} diff --git a/mojo/public/tools/bindings/generators/js_templates/validation_macros.tmpl b/mojo/public/tools/bindings/generators/js_templates/validation_macros.tmpl new file mode 100644 index 0000000..7a39749 --- /dev/null +++ b/mojo/public/tools/bindings/generators/js_templates/validation_macros.tmpl @@ -0,0 +1,52 @@ +{% macro _check_err() -%} +if (err !== validator.validationError.NONE) + return err; +{%- endmacro %} + +{%- macro _validate_field(field, offset, name) %} +{%- if field|is_string_pointer_field %} +// validate {{name}} +err = messageValidator.validateStringPointer({{offset}}, {{field|validate_string_params}}) +{{_check_err()}} +{%- elif field|is_array_pointer_field %} +// validate {{name}} +err = messageValidator.validateArrayPointer({{offset}}, {{field|validate_array_params}}); +{{_check_err()}} +{%- elif field|is_struct_pointer_field %} +// validate {{name}} +err = messageValidator.validateStructPointer({{offset}}, {{field|validate_struct_params}}); +{{_check_err()}} +{%- elif field|is_map_pointer_field %} +// validate {{name}} +err = messageValidator.validateMapPointer({{offset}}, {{field|validate_map_params}}); +{{_check_err()}} +{%- elif field|is_interface_field %} +// validate {{name}} +err = messageValidator.validateInterface({{offset}}, {{field|validate_interface_params}}); +{{_check_err()}} +{%- elif field|is_handle_field %} +// validate {{name}} +err = messageValidator.validateHandle({{offset}}, {{field|validate_handle_params}}) +{{_check_err()}} +{%- endif %} +{%- endmacro %} + +{%- macro validate_struct_field(field, offset, name) %} +{%- if field|is_union_field %} +// validate {{name}} +err = messageValidator.validateUnion({{offset}}, {{field|validate_union_params}}); +{{_check_err()}} +{%- else %} +{{_validate_field(field, offset, name)}} +{%- endif %} +{%- endmacro %} + +{%- macro validate_union_field(field, offset, name) %} +{%- if field|is_union_field %} +// validate {{name}} +err = messageValidator.validateNestedUnion({{offset}}, {{field|validate_union_params}}); +{{_check_err()}} +{%- else %} +{{_validate_field(field, offset, name)}} +{%- endif %} +{%- endmacro %} diff --git a/mojo/public/tools/bindings/generators/mojom_cpp_generator.py b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py new file mode 100644 index 0000000..5a3b59e --- /dev/null +++ b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py @@ -0,0 +1,451 @@ +# 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. + +"""Generates C++ source files from a mojom.Module.""" + +import mojom.generate.generator as generator +import mojom.generate.module as mojom +import mojom.generate.pack as pack +from mojom.generate.template_expander import UseJinja + + +_kind_to_cpp_type = { + mojom.BOOL: "bool", + mojom.INT8: "int8_t", + mojom.UINT8: "uint8_t", + mojom.INT16: "int16_t", + mojom.UINT16: "uint16_t", + mojom.INT32: "int32_t", + mojom.UINT32: "uint32_t", + mojom.FLOAT: "float", + mojom.HANDLE: "mojo::Handle", + mojom.DCPIPE: "mojo::DataPipeConsumerHandle", + mojom.DPPIPE: "mojo::DataPipeProducerHandle", + mojom.MSGPIPE: "mojo::MessagePipeHandle", + mojom.SHAREDBUFFER: "mojo::SharedBufferHandle", + mojom.NULLABLE_HANDLE: "mojo::Handle", + mojom.NULLABLE_DCPIPE: "mojo::DataPipeConsumerHandle", + mojom.NULLABLE_DPPIPE: "mojo::DataPipeProducerHandle", + mojom.NULLABLE_MSGPIPE: "mojo::MessagePipeHandle", + mojom.NULLABLE_SHAREDBUFFER: "mojo::SharedBufferHandle", + mojom.INT64: "int64_t", + mojom.UINT64: "uint64_t", + mojom.DOUBLE: "double", +} + +_kind_to_cpp_literal_suffix = { + mojom.UINT8: "U", + mojom.UINT16: "U", + mojom.UINT32: "U", + mojom.FLOAT: "f", + mojom.UINT64: "ULL", +} + +def ConstantValue(constant): + return ExpressionToText(constant.value, kind=constant.kind) + +def DefaultValue(field): + if field.default: + if mojom.IsStructKind(field.kind): + assert field.default == "default" + return "%s::New()" % GetNameForKind(field.kind) + return ExpressionToText(field.default, kind=field.kind) + return "" + +def NamespaceToArray(namespace): + return namespace.split(".") if namespace else [] + +def GetNameForKind(kind, internal = False): + parts = [] + if kind.imported_from: + parts.extend(NamespaceToArray(kind.imported_from["namespace"])) + if internal: + parts.append("internal") + if kind.parent_kind: + parts.append(kind.parent_kind.name) + parts.append(kind.name) + return "::".join(parts) + +def GetCppType(kind): + if mojom.IsArrayKind(kind): + return "mojo::internal::Array_Data<%s>*" % GetCppType(kind.kind) + if mojom.IsMapKind(kind): + return "mojo::internal::Map_Data<%s, %s>*" % ( + GetCppType(kind.key_kind), GetCppType(kind.value_kind)) + if mojom.IsStructKind(kind): + return "%s_Data*" % GetNameForKind(kind, internal=True) + if mojom.IsUnionKind(kind): + return "%s_Data" % GetNameForKind(kind, internal=True) + if mojom.IsInterfaceKind(kind): + return "mojo::internal::Interface_Data" + if mojom.IsInterfaceRequestKind(kind): + return "mojo::MessagePipeHandle" + if mojom.IsAssociatedInterfaceKind(kind): + return "mojo::internal::AssociatedInterface_Data" + if mojom.IsAssociatedInterfaceRequestKind(kind): + return "mojo::internal::AssociatedInterfaceRequest_Data" + if mojom.IsEnumKind(kind): + return "int32_t" + if mojom.IsStringKind(kind): + return "mojo::internal::String_Data*" + return _kind_to_cpp_type[kind] + +def GetCppPodType(kind): + if mojom.IsStringKind(kind): + return "char*" + return _kind_to_cpp_type[kind] + +def GetCppArrayArgWrapperType(kind): + if mojom.IsEnumKind(kind): + return GetNameForKind(kind) + if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind): + return "%sPtr" % GetNameForKind(kind) + if mojom.IsArrayKind(kind): + return "mojo::Array<%s> " % GetCppArrayArgWrapperType(kind.kind) + if mojom.IsMapKind(kind): + return "mojo::Map<%s, %s> " % (GetCppArrayArgWrapperType(kind.key_kind), + GetCppArrayArgWrapperType(kind.value_kind)) + if mojom.IsInterfaceKind(kind): + raise Exception("Arrays of interfaces not yet supported!") + if mojom.IsInterfaceRequestKind(kind): + raise Exception("Arrays of interface requests not yet supported!") + if mojom.IsAssociatedInterfaceKind(kind): + raise Exception("Arrays of associated interfaces not yet supported!") + if mojom.IsAssociatedInterfaceRequestKind(kind): + raise Exception("Arrays of associated interface requests not yet " + "supported!") + if mojom.IsStringKind(kind): + return "mojo::String" + if mojom.IsGenericHandleKind(kind): + return "mojo::ScopedHandle" + if mojom.IsDataPipeConsumerKind(kind): + return "mojo::ScopedDataPipeConsumerHandle" + if mojom.IsDataPipeProducerKind(kind): + return "mojo::ScopedDataPipeProducerHandle" + if mojom.IsMessagePipeKind(kind): + return "mojo::ScopedMessagePipeHandle" + if mojom.IsSharedBufferKind(kind): + return "mojo::ScopedSharedBufferHandle" + return _kind_to_cpp_type[kind] + +def GetCppResultWrapperType(kind): + if mojom.IsEnumKind(kind): + return GetNameForKind(kind) + if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind): + return "%sPtr" % GetNameForKind(kind) + if mojom.IsArrayKind(kind): + return "mojo::Array<%s>" % GetCppArrayArgWrapperType(kind.kind) + if mojom.IsMapKind(kind): + return "mojo::Map<%s, %s>" % (GetCppArrayArgWrapperType(kind.key_kind), + GetCppArrayArgWrapperType(kind.value_kind)) + if mojom.IsInterfaceKind(kind): + return "%sPtr" % GetNameForKind(kind) + if mojom.IsInterfaceRequestKind(kind): + return "mojo::InterfaceRequest<%s>" % GetNameForKind(kind.kind) + if mojom.IsAssociatedInterfaceKind(kind): + return "mojo::AssociatedInterfacePtrInfo<%s>" % GetNameForKind(kind.kind) + if mojom.IsAssociatedInterfaceRequestKind(kind): + return "mojo::AssociatedInterfaceRequest<%s>" % GetNameForKind(kind.kind) + if mojom.IsStringKind(kind): + return "mojo::String" + if mojom.IsGenericHandleKind(kind): + return "mojo::ScopedHandle" + if mojom.IsDataPipeConsumerKind(kind): + return "mojo::ScopedDataPipeConsumerHandle" + if mojom.IsDataPipeProducerKind(kind): + return "mojo::ScopedDataPipeProducerHandle" + if mojom.IsMessagePipeKind(kind): + return "mojo::ScopedMessagePipeHandle" + if mojom.IsSharedBufferKind(kind): + return "mojo::ScopedSharedBufferHandle" + # TODO(rudominer) After improvements to compiler front end have landed, + # revisit strategy used below for emitting a useful error message when an + # undefined identifier is referenced. + val = _kind_to_cpp_type.get(kind) + if (val is not None): + return val + raise Exception("Unrecognized kind %s" % kind.spec) + +def GetCppWrapperType(kind): + if mojom.IsEnumKind(kind): + return GetNameForKind(kind) + if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind): + return "%sPtr" % GetNameForKind(kind) + if mojom.IsArrayKind(kind): + return "mojo::Array<%s>" % GetCppArrayArgWrapperType(kind.kind) + if mojom.IsMapKind(kind): + return "mojo::Map<%s, %s>" % (GetCppArrayArgWrapperType(kind.key_kind), + GetCppArrayArgWrapperType(kind.value_kind)) + if mojom.IsInterfaceKind(kind): + return "%sPtr" % GetNameForKind(kind) + if mojom.IsInterfaceRequestKind(kind): + return "mojo::InterfaceRequest<%s>" % GetNameForKind(kind.kind) + if mojom.IsAssociatedInterfaceKind(kind): + return "mojo::AssociatedInterfacePtrInfo<%s>" % GetNameForKind(kind.kind) + if mojom.IsAssociatedInterfaceRequestKind(kind): + return "mojo::AssociatedInterfaceRequest<%s>" % GetNameForKind(kind.kind) + if mojom.IsStringKind(kind): + return "mojo::String" + if mojom.IsGenericHandleKind(kind): + return "mojo::ScopedHandle" + if mojom.IsDataPipeConsumerKind(kind): + return "mojo::ScopedDataPipeConsumerHandle" + if mojom.IsDataPipeProducerKind(kind): + return "mojo::ScopedDataPipeProducerHandle" + if mojom.IsMessagePipeKind(kind): + return "mojo::ScopedMessagePipeHandle" + if mojom.IsSharedBufferKind(kind): + return "mojo::ScopedSharedBufferHandle" + return _kind_to_cpp_type[kind] + +def GetCppConstWrapperType(kind): + if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind): + return "%sPtr" % GetNameForKind(kind) + if mojom.IsArrayKind(kind): + return "mojo::Array<%s>" % GetCppArrayArgWrapperType(kind.kind) + if mojom.IsMapKind(kind): + return "mojo::Map<%s, %s>" % (GetCppArrayArgWrapperType(kind.key_kind), + GetCppArrayArgWrapperType(kind.value_kind)) + if mojom.IsInterfaceKind(kind): + return "%sPtr" % GetNameForKind(kind) + if mojom.IsInterfaceRequestKind(kind): + return "mojo::InterfaceRequest<%s>" % GetNameForKind(kind.kind) + if mojom.IsAssociatedInterfaceKind(kind): + return "mojo::AssociatedInterfacePtrInfo<%s>" % GetNameForKind(kind.kind) + if mojom.IsAssociatedInterfaceRequestKind(kind): + return "mojo::AssociatedInterfaceRequest<%s>" % GetNameForKind(kind.kind) + if mojom.IsEnumKind(kind): + return GetNameForKind(kind) + if mojom.IsStringKind(kind): + return "const mojo::String&" + if mojom.IsGenericHandleKind(kind): + return "mojo::ScopedHandle" + if mojom.IsDataPipeConsumerKind(kind): + return "mojo::ScopedDataPipeConsumerHandle" + if mojom.IsDataPipeProducerKind(kind): + return "mojo::ScopedDataPipeProducerHandle" + if mojom.IsMessagePipeKind(kind): + return "mojo::ScopedMessagePipeHandle" + if mojom.IsSharedBufferKind(kind): + return "mojo::ScopedSharedBufferHandle" + if not kind in _kind_to_cpp_type: + print "missing:", kind.spec + return _kind_to_cpp_type[kind] + +def GetCppFieldType(kind): + if mojom.IsStructKind(kind): + return ("mojo::internal::StructPointer<%s_Data>" % + GetNameForKind(kind, internal=True)) + if mojom.IsUnionKind(kind): + return "%s_Data" % GetNameForKind(kind, internal=True) + if mojom.IsArrayKind(kind): + return "mojo::internal::ArrayPointer<%s>" % GetCppType(kind.kind) + if mojom.IsMapKind(kind): + return ("mojo::internal::StructPointer<mojo::internal::Map_Data<%s, %s>>" % + (GetCppType(kind.key_kind), GetCppType(kind.value_kind))) + if mojom.IsInterfaceKind(kind): + return "mojo::internal::Interface_Data" + if mojom.IsInterfaceRequestKind(kind): + return "mojo::MessagePipeHandle" + if mojom.IsAssociatedInterfaceKind(kind): + return "mojo::internal::AssociatedInterface_Data" + if mojom.IsAssociatedInterfaceRequestKind(kind): + return "mojo::internal::AssociatedInterfaceRequest_Data" + if mojom.IsEnumKind(kind): + return GetNameForKind(kind) + if mojom.IsStringKind(kind): + return "mojo::internal::StringPointer" + return _kind_to_cpp_type[kind] + +def GetCppUnionFieldType(kind): + if mojom.IsAnyHandleKind(kind): + return "MojoHandle" + if mojom.IsInterfaceKind(kind): + return "uint64_t" + if mojom.IsEnumKind(kind): + return "int32_t" + if mojom.IsUnionKind(kind): + return ("mojo::internal::UnionPointer<%s_Data>" % + GetNameForKind(kind, internal=True)) + return GetCppFieldType(kind) + +def GetUnionGetterReturnType(kind): + if (mojom.IsStructKind(kind) or mojom.IsUnionKind(kind) or + mojom.IsArrayKind(kind) or mojom.IsMapKind(kind) or + mojom.IsAnyHandleKind(kind) or mojom.IsInterfaceKind(kind) + or mojom.IsAssociatedKind(kind)): + return "%s&" % GetCppWrapperType(kind) + return GetCppResultWrapperType(kind) + +def TranslateConstants(token, kind): + if isinstance(token, mojom.NamedValue): + # Both variable and enum constants are constructed like: + # Namespace::Struct::CONSTANT_NAME + # For enums, CONSTANT_NAME is ENUM_NAME_ENUM_VALUE. + name = [] + if token.imported_from: + name.extend(NamespaceToArray(token.namespace)) + if token.parent_kind: + name.append(token.parent_kind.name) + if isinstance(token, mojom.EnumValue): + name.append( + "%s_%s" % (generator.CamelCaseToAllCaps(token.enum.name), token.name)) + else: + name.append(token.name) + return "::".join(name) + + if isinstance(token, mojom.BuiltinValue): + if token.value == "double.INFINITY" or token.value == "float.INFINITY": + return "INFINITY"; + if token.value == "double.NEGATIVE_INFINITY" or \ + token.value == "float.NEGATIVE_INFINITY": + return "-INFINITY"; + if token.value == "double.NAN" or token.value == "float.NAN": + return "NAN"; + + if (kind is not None and mojom.IsFloatKind(kind)): + return token if token.isdigit() else token + "f"; + + # Per C++11, 2.14.2, the type of an integer literal is the first of the + # corresponding list in Table 6 in which its value can be represented. In this + # case, the list for decimal constants with no suffix is: + # int, long int, long long int + # The standard considers a program ill-formed if it contains an integer + # literal that cannot be represented by any of the allowed types. + # + # As it turns out, MSVC doesn't bother trying to fall back to long long int, + # so the integral constant -2147483648 causes it grief: it decides to + # represent 2147483648 as an unsigned integer, and then warns that the unary + # minus operator doesn't make sense on unsigned types. Doh! + if kind == mojom.INT32 and token == "-2147483648": + return "(-%d - 1) /* %s */" % ( + 2**31 - 1, "Workaround for MSVC bug; see https://crbug.com/445618") + + return "%s%s" % (token, _kind_to_cpp_literal_suffix.get(kind, "")) + +def ExpressionToText(value, kind=None): + return TranslateConstants(value, kind) + +def ShouldInlineStruct(struct): + # TODO(darin): Base this on the size of the wrapper class. + if len(struct.fields) > 4: + return False + for field in struct.fields: + if mojom.IsMoveOnlyKind(field.kind): + return False + return True + +def ShouldInlineUnion(union): + return not any(mojom.IsMoveOnlyKind(field.kind) for field in union.fields) + +def GetArrayValidateParamsCtorArgs(kind): + if mojom.IsStringKind(kind): + expected_num_elements = 0 + element_is_nullable = False + element_validate_params = "nullptr" + elif mojom.IsMapKind(kind): + expected_num_elements = 0 + element_is_nullable = mojom.IsNullableKind(kind.value_kind) + element_validate_params = GetNewArrayValidateParams(kind.value_kind) + else: + expected_num_elements = generator.ExpectedArraySize(kind) or 0 + element_is_nullable = mojom.IsNullableKind(kind.kind) + element_validate_params = GetNewArrayValidateParams(kind.kind) + + return "%d, %s, %s" % (expected_num_elements, + "true" if element_is_nullable else "false", + element_validate_params) + +def GetNewArrayValidateParams(kind): + if (not mojom.IsArrayKind(kind) and not mojom.IsMapKind(kind) and + not mojom.IsStringKind(kind)): + return "nullptr" + + return "new mojo::internal::ArrayValidateParams(%s)" % ( + GetArrayValidateParamsCtorArgs(kind)) + +def GetMapValidateParamsCtorArgs(value_kind): + # Unlike GetArrayValidateParams, we are given the wrapped kind, instead of + # the raw array kind. So we wrap the return value of GetArrayValidateParams. + element_is_nullable = mojom.IsNullableKind(value_kind) + return "0, %s, %s" % ("true" if element_is_nullable else "false", + GetNewArrayValidateParams(value_kind)) + +class Generator(generator.Generator): + + cpp_filters = { + "constant_value": ConstantValue, + "cpp_const_wrapper_type": GetCppConstWrapperType, + "cpp_field_type": GetCppFieldType, + "cpp_union_field_type": GetCppUnionFieldType, + "cpp_pod_type": GetCppPodType, + "cpp_result_type": GetCppResultWrapperType, + "cpp_type": GetCppType, + "cpp_union_getter_return_type": GetUnionGetterReturnType, + "cpp_wrapper_type": GetCppWrapperType, + "default_value": DefaultValue, + "expression_to_text": ExpressionToText, + "get_array_validate_params_ctor_args": GetArrayValidateParamsCtorArgs, + "get_map_validate_params_ctor_args": GetMapValidateParamsCtorArgs, + "get_name_for_kind": GetNameForKind, + "get_pad": pack.GetPad, + "has_callbacks": mojom.HasCallbacks, + "should_inline": ShouldInlineStruct, + "should_inline_union": ShouldInlineUnion, + "is_array_kind": mojom.IsArrayKind, + "is_cloneable_kind": mojom.IsCloneableKind, + "is_enum_kind": mojom.IsEnumKind, + "is_integral_kind": mojom.IsIntegralKind, + "is_move_only_kind": mojom.IsMoveOnlyKind, + "is_any_handle_kind": mojom.IsAnyHandleKind, + "is_interface_kind": mojom.IsInterfaceKind, + "is_interface_request_kind": mojom.IsInterfaceRequestKind, + "is_associated_interface_kind": mojom.IsAssociatedInterfaceKind, + "is_associated_interface_request_kind": + mojom.IsAssociatedInterfaceRequestKind, + "is_associated_kind": mojom.IsAssociatedKind, + "is_map_kind": mojom.IsMapKind, + "is_nullable_kind": mojom.IsNullableKind, + "is_object_kind": mojom.IsObjectKind, + "is_string_kind": mojom.IsStringKind, + "is_struct_kind": mojom.IsStructKind, + "is_union_kind": mojom.IsUnionKind, + "struct_size": lambda ps: ps.GetTotalSize() + _HEADER_SIZE, + "stylize_method": generator.StudlyCapsToCamel, + "to_all_caps": generator.CamelCaseToAllCaps, + "under_to_camel": generator.UnderToCamel, + } + + def GetJinjaExports(self): + return { + "module": self.module, + "namespace": self.module.namespace, + "namespaces_as_array": NamespaceToArray(self.module.namespace), + "imports": self.module.imports, + "kinds": self.module.kinds, + "enums": self.module.enums, + "structs": self.GetStructs(), + "unions": self.GetUnions(), + "interfaces": self.GetInterfaces(), + } + + @UseJinja("cpp_templates/module.h.tmpl", filters=cpp_filters) + def GenerateModuleHeader(self): + return self.GetJinjaExports() + + @UseJinja("cpp_templates/module-internal.h.tmpl", filters=cpp_filters) + def GenerateModuleInternalHeader(self): + return self.GetJinjaExports() + + @UseJinja("cpp_templates/module.cc.tmpl", filters=cpp_filters) + def GenerateModuleSource(self): + return self.GetJinjaExports() + + def GenerateFiles(self, args): + self.Write(self.GenerateModuleHeader(), + self.MatchMojomFilePath("%s.h" % self.module.name)) + self.Write(self.GenerateModuleInternalHeader(), + self.MatchMojomFilePath("%s-internal.h" % self.module.name)) + self.Write(self.GenerateModuleSource(), + self.MatchMojomFilePath("%s.cc" % self.module.name)) diff --git a/mojo/public/tools/bindings/generators/mojom_java_generator.py b/mojo/public/tools/bindings/generators/mojom_java_generator.py new file mode 100644 index 0000000..c091a5f --- /dev/null +++ b/mojo/public/tools/bindings/generators/mojom_java_generator.py @@ -0,0 +1,535 @@ +# 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. + +"""Generates java source files from a mojom.Module.""" + +import argparse +import ast +import contextlib +import os +import re +import shutil +import tempfile +import zipfile + +from jinja2 import contextfilter + +import mojom.fileutil as fileutil +import mojom.generate.generator as generator +import mojom.generate.module as mojom +from mojom.generate.template_expander import UseJinja + + +GENERATOR_PREFIX = 'java' + +_spec_to_java_type = { + mojom.BOOL.spec: 'boolean', + mojom.DCPIPE.spec: 'org.chromium.mojo.system.DataPipe.ConsumerHandle', + mojom.DOUBLE.spec: 'double', + mojom.DPPIPE.spec: 'org.chromium.mojo.system.DataPipe.ProducerHandle', + mojom.FLOAT.spec: 'float', + mojom.HANDLE.spec: 'org.chromium.mojo.system.UntypedHandle', + mojom.INT16.spec: 'short', + mojom.INT32.spec: 'int', + mojom.INT64.spec: 'long', + mojom.INT8.spec: 'byte', + mojom.MSGPIPE.spec: 'org.chromium.mojo.system.MessagePipeHandle', + mojom.NULLABLE_DCPIPE.spec: + 'org.chromium.mojo.system.DataPipe.ConsumerHandle', + mojom.NULLABLE_DPPIPE.spec: + 'org.chromium.mojo.system.DataPipe.ProducerHandle', + mojom.NULLABLE_HANDLE.spec: 'org.chromium.mojo.system.UntypedHandle', + mojom.NULLABLE_MSGPIPE.spec: 'org.chromium.mojo.system.MessagePipeHandle', + mojom.NULLABLE_SHAREDBUFFER.spec: + 'org.chromium.mojo.system.SharedBufferHandle', + mojom.NULLABLE_STRING.spec: 'String', + mojom.SHAREDBUFFER.spec: 'org.chromium.mojo.system.SharedBufferHandle', + mojom.STRING.spec: 'String', + mojom.UINT16.spec: 'short', + mojom.UINT32.spec: 'int', + mojom.UINT64.spec: 'long', + mojom.UINT8.spec: 'byte', +} + +_spec_to_decode_method = { + mojom.BOOL.spec: 'readBoolean', + mojom.DCPIPE.spec: 'readConsumerHandle', + mojom.DOUBLE.spec: 'readDouble', + mojom.DPPIPE.spec: 'readProducerHandle', + mojom.FLOAT.spec: 'readFloat', + mojom.HANDLE.spec: 'readUntypedHandle', + mojom.INT16.spec: 'readShort', + mojom.INT32.spec: 'readInt', + mojom.INT64.spec: 'readLong', + mojom.INT8.spec: 'readByte', + mojom.MSGPIPE.spec: 'readMessagePipeHandle', + mojom.NULLABLE_DCPIPE.spec: 'readConsumerHandle', + mojom.NULLABLE_DPPIPE.spec: 'readProducerHandle', + mojom.NULLABLE_HANDLE.spec: 'readUntypedHandle', + mojom.NULLABLE_MSGPIPE.spec: 'readMessagePipeHandle', + mojom.NULLABLE_SHAREDBUFFER.spec: 'readSharedBufferHandle', + mojom.NULLABLE_STRING.spec: 'readString', + mojom.SHAREDBUFFER.spec: 'readSharedBufferHandle', + mojom.STRING.spec: 'readString', + mojom.UINT16.spec: 'readShort', + mojom.UINT32.spec: 'readInt', + mojom.UINT64.spec: 'readLong', + mojom.UINT8.spec: 'readByte', +} + +_java_primitive_to_boxed_type = { + 'boolean': 'Boolean', + 'byte': 'Byte', + 'double': 'Double', + 'float': 'Float', + 'int': 'Integer', + 'long': 'Long', + 'short': 'Short', +} + + +def NameToComponent(name): + # insert '_' between anything and a Title name (e.g, HTTPEntry2FooBar -> + # HTTP_Entry2_FooBar) + name = re.sub('([^_])([A-Z][^A-Z_]+)', r'\1_\2', name) + # insert '_' between non upper and start of upper blocks (e.g., + # HTTP_Entry2_FooBar -> HTTP_Entry2_Foo_Bar) + name = re.sub('([^A-Z_])([A-Z])', r'\1_\2', name) + return [x.lower() for x in name.split('_')] + +def UpperCamelCase(name): + return ''.join([x.capitalize() for x in NameToComponent(name)]) + +def CamelCase(name): + uccc = UpperCamelCase(name) + return uccc[0].lower() + uccc[1:] + +def ConstantStyle(name): + components = NameToComponent(name) + if components[0] == 'k' and len(components) > 1: + components = components[1:] + # variable cannot starts with a digit. + if components[0][0].isdigit(): + components[0] = '_' + components[0] + return '_'.join([x.upper() for x in components]) + +def GetNameForElement(element): + if (mojom.IsEnumKind(element) or mojom.IsInterfaceKind(element) or + mojom.IsStructKind(element) or mojom.IsUnionKind(element)): + return UpperCamelCase(element.name) + if mojom.IsInterfaceRequestKind(element) or mojom.IsAssociatedKind(element): + return GetNameForElement(element.kind) + if isinstance(element, (mojom.Method, + mojom.Parameter, + mojom.Field)): + return CamelCase(element.name) + if isinstance(element, mojom.EnumValue): + return (GetNameForElement(element.enum) + '.' + + ConstantStyle(element.name)) + if isinstance(element, (mojom.NamedValue, + mojom.Constant, + mojom.EnumField)): + return ConstantStyle(element.name) + raise Exception('Unexpected element: %s' % element) + +def GetInterfaceResponseName(method): + return UpperCamelCase(method.name + 'Response') + +def ParseStringAttribute(attribute): + assert isinstance(attribute, basestring) + return attribute + +def GetJavaTrueFalse(value): + return 'true' if value else 'false' + +def GetArrayNullabilityFlags(kind): + """Returns nullability flags for an array type, see Decoder.java. + + As we have dedicated decoding functions for arrays, we have to pass + nullability information about both the array itself, as well as the array + element type there. + """ + assert mojom.IsArrayKind(kind) + ARRAY_NULLABLE = \ + 'org.chromium.mojo.bindings.BindingsHelper.ARRAY_NULLABLE' + ELEMENT_NULLABLE = \ + 'org.chromium.mojo.bindings.BindingsHelper.ELEMENT_NULLABLE' + NOTHING_NULLABLE = \ + 'org.chromium.mojo.bindings.BindingsHelper.NOTHING_NULLABLE' + + flags_to_set = [] + if mojom.IsNullableKind(kind): + flags_to_set.append(ARRAY_NULLABLE) + if mojom.IsNullableKind(kind.kind): + flags_to_set.append(ELEMENT_NULLABLE) + + if not flags_to_set: + flags_to_set = [NOTHING_NULLABLE] + return ' | '.join(flags_to_set) + + +def AppendEncodeDecodeParams(initial_params, context, kind, bit): + """ Appends standard parameters shared between encode and decode calls. """ + params = list(initial_params) + if (kind == mojom.BOOL): + params.append(str(bit)) + if mojom.IsReferenceKind(kind): + if mojom.IsArrayKind(kind): + params.append(GetArrayNullabilityFlags(kind)) + else: + params.append(GetJavaTrueFalse(mojom.IsNullableKind(kind))) + if mojom.IsArrayKind(kind): + params.append(GetArrayExpectedLength(kind)) + if mojom.IsInterfaceKind(kind): + params.append('%s.MANAGER' % GetJavaType(context, kind)) + if mojom.IsArrayKind(kind) and mojom.IsInterfaceKind(kind.kind): + params.append('%s.MANAGER' % GetJavaType(context, kind.kind)) + return params + + +@contextfilter +def DecodeMethod(context, kind, offset, bit): + def _DecodeMethodName(kind): + if mojom.IsArrayKind(kind): + return _DecodeMethodName(kind.kind) + 's' + if mojom.IsEnumKind(kind): + return _DecodeMethodName(mojom.INT32) + if mojom.IsInterfaceRequestKind(kind): + return 'readInterfaceRequest' + if mojom.IsInterfaceKind(kind): + return 'readServiceInterface' + if mojom.IsAssociatedInterfaceRequestKind(kind): + return 'readAssociatedInterfaceRequestNotSupported' + if mojom.IsAssociatedInterfaceKind(kind): + return 'readAssociatedServiceInterfaceNotSupported' + return _spec_to_decode_method[kind.spec] + methodName = _DecodeMethodName(kind) + params = AppendEncodeDecodeParams([ str(offset) ], context, kind, bit) + return '%s(%s)' % (methodName, ', '.join(params)) + +@contextfilter +def EncodeMethod(context, kind, variable, offset, bit): + params = AppendEncodeDecodeParams( + [ variable, str(offset) ], context, kind, bit) + return 'encode(%s)' % ', '.join(params) + +def GetPackage(module): + if module.attributes and 'JavaPackage' in module.attributes: + return ParseStringAttribute(module.attributes['JavaPackage']) + # Default package. + if module.namespace: + return 'org.chromium.mojom.' + module.namespace + return 'org.chromium.mojom' + +def GetNameForKind(context, kind): + def _GetNameHierachy(kind): + hierachy = [] + if kind.parent_kind: + hierachy = _GetNameHierachy(kind.parent_kind) + hierachy.append(GetNameForElement(kind)) + return hierachy + + module = context.resolve('module') + elements = [] + if GetPackage(module) != GetPackage(kind.module): + elements += [GetPackage(kind.module)] + elements += _GetNameHierachy(kind) + return '.'.join(elements) + +def GetBoxedJavaType(context, kind, with_generics=True): + unboxed_type = GetJavaType(context, kind, False, with_generics) + if unboxed_type in _java_primitive_to_boxed_type: + return _java_primitive_to_boxed_type[unboxed_type] + return unboxed_type + +@contextfilter +def GetJavaType(context, kind, boxed=False, with_generics=True): + if boxed: + return GetBoxedJavaType(context, kind) + if (mojom.IsStructKind(kind) or + mojom.IsInterfaceKind(kind) or + mojom.IsUnionKind(kind)): + return GetNameForKind(context, kind) + if mojom.IsInterfaceRequestKind(kind): + return ('org.chromium.mojo.bindings.InterfaceRequest<%s>' % + GetNameForKind(context, kind.kind)) + if mojom.IsAssociatedInterfaceKind(kind): + return 'org.chromium.mojo.bindings.AssociatedInterfaceNotSupported' + if mojom.IsAssociatedInterfaceRequestKind(kind): + return 'org.chromium.mojo.bindings.AssociatedInterfaceRequestNotSupported' + if mojom.IsMapKind(kind): + if with_generics: + return 'java.util.Map<%s, %s>' % ( + GetBoxedJavaType(context, kind.key_kind), + GetBoxedJavaType(context, kind.value_kind)) + else: + return 'java.util.Map' + if mojom.IsArrayKind(kind): + return '%s[]' % GetJavaType(context, kind.kind, boxed, with_generics) + if mojom.IsEnumKind(kind): + return 'int' + return _spec_to_java_type[kind.spec] + +@contextfilter +def DefaultValue(context, field): + assert field.default + if isinstance(field.kind, mojom.Struct): + assert field.default == 'default' + return 'new %s()' % GetJavaType(context, field.kind) + return '(%s) %s' % ( + GetJavaType(context, field.kind), + ExpressionToText(context, field.default, kind_spec=field.kind.spec)) + +@contextfilter +def ConstantValue(context, constant): + return '(%s) %s' % ( + GetJavaType(context, constant.kind), + ExpressionToText(context, constant.value, kind_spec=constant.kind.spec)) + +@contextfilter +def NewArray(context, kind, size): + if mojom.IsArrayKind(kind.kind): + return NewArray(context, kind.kind, size) + '[]' + return 'new %s[%s]' % ( + GetJavaType(context, kind.kind, boxed=False, with_generics=False), size) + +@contextfilter +def ExpressionToText(context, token, kind_spec=''): + def _TranslateNamedValue(named_value): + entity_name = GetNameForElement(named_value) + if named_value.parent_kind: + return GetJavaType(context, named_value.parent_kind) + '.' + entity_name + # Handle the case where named_value is a module level constant: + if not isinstance(named_value, mojom.EnumValue): + entity_name = (GetConstantsMainEntityName(named_value.module) + '.' + + entity_name) + if GetPackage(named_value.module) == GetPackage(context.resolve('module')): + return entity_name + return GetPackage(named_value.module) + '.' + entity_name + + if isinstance(token, mojom.NamedValue): + return _TranslateNamedValue(token) + if kind_spec.startswith('i') or kind_spec.startswith('u'): + # Add Long suffix to all integer literals. + number = ast.literal_eval(token.lstrip('+ ')) + if not isinstance(number, (int, long)): + raise ValueError('got unexpected type %r for int literal %r' % ( + type(number), token)) + # If the literal is too large to fit a signed long, convert it to the + # equivalent signed long. + if number >= 2 ** 63: + number -= 2 ** 64 + return '%dL' % number + if isinstance(token, mojom.BuiltinValue): + if token.value == 'double.INFINITY': + return 'java.lang.Double.POSITIVE_INFINITY' + if token.value == 'double.NEGATIVE_INFINITY': + return 'java.lang.Double.NEGATIVE_INFINITY' + if token.value == 'double.NAN': + return 'java.lang.Double.NaN' + if token.value == 'float.INFINITY': + return 'java.lang.Float.POSITIVE_INFINITY' + if token.value == 'float.NEGATIVE_INFINITY': + return 'java.lang.Float.NEGATIVE_INFINITY' + if token.value == 'float.NAN': + return 'java.lang.Float.NaN' + return token + +def GetArrayKind(kind, size = None): + if size is None: + return mojom.Array(kind) + else: + array = mojom.Array(kind, 0) + array.java_map_size = size + return array + +def GetArrayExpectedLength(kind): + if mojom.IsArrayKind(kind) and kind.length is not None: + return getattr(kind, 'java_map_size', str(kind.length)) + else: + return 'org.chromium.mojo.bindings.BindingsHelper.UNSPECIFIED_ARRAY_LENGTH' + +def IsPointerArrayKind(kind): + if not mojom.IsArrayKind(kind): + return False + sub_kind = kind.kind + return mojom.IsObjectKind(sub_kind) and not mojom.IsUnionKind(sub_kind) + +def IsUnionArrayKind(kind): + if not mojom.IsArrayKind(kind): + return False + sub_kind = kind.kind + return mojom.IsUnionKind(sub_kind) + +def GetConstantsMainEntityName(module): + if module.attributes and 'JavaConstantsClassName' in module.attributes: + return ParseStringAttribute(module.attributes['JavaConstantsClassName']) + # This constructs the name of the embedding classes for module level constants + # by extracting the mojom's filename and prepending it to Constants. + return (UpperCamelCase(module.path.split('/')[-1].rsplit('.', 1)[0]) + + 'Constants') + +def GetMethodOrdinalName(method): + return ConstantStyle(method.name) + '_ORDINAL' + +def HasMethodWithResponse(interface): + for method in interface.methods: + if method.response_parameters is not None: + return True + return False + +def HasMethodWithoutResponse(interface): + for method in interface.methods: + if method.response_parameters is None: + return True + return False + +@contextlib.contextmanager +def TempDir(): + dirname = tempfile.mkdtemp() + try: + yield dirname + finally: + shutil.rmtree(dirname) + +def ZipContentInto(root, zip_filename): + with zipfile.ZipFile(zip_filename, 'w') as zip_file: + for dirname, _, files in os.walk(root): + for filename in files: + path = os.path.join(dirname, filename) + path_in_archive = os.path.relpath(path, root) + zip_file.write(path, path_in_archive) + +class Generator(generator.Generator): + + java_filters = { + 'array_expected_length': GetArrayExpectedLength, + 'array': GetArrayKind, + 'constant_value': ConstantValue, + 'decode_method': DecodeMethod, + 'default_value': DefaultValue, + 'encode_method': EncodeMethod, + 'expression_to_text': ExpressionToText, + 'has_method_without_response': HasMethodWithoutResponse, + 'has_method_with_response': HasMethodWithResponse, + 'interface_response_name': GetInterfaceResponseName, + 'is_array_kind': mojom.IsArrayKind, + 'is_any_handle_kind': mojom.IsAnyHandleKind, + 'is_interface_request_kind': mojom.IsInterfaceRequestKind, + 'is_map_kind': mojom.IsMapKind, + 'is_nullable_kind': mojom.IsNullableKind, + 'is_pointer_array_kind': IsPointerArrayKind, + 'is_reference_kind': mojom.IsReferenceKind, + 'is_struct_kind': mojom.IsStructKind, + 'is_union_array_kind': IsUnionArrayKind, + 'is_union_kind': mojom.IsUnionKind, + 'java_true_false': GetJavaTrueFalse, + 'java_type': GetJavaType, + 'method_ordinal_name': GetMethodOrdinalName, + 'name': GetNameForElement, + 'new_array': NewArray, + 'ucc': lambda x: UpperCamelCase(x.name), + } + + def GetJinjaExports(self): + return { + 'package': GetPackage(self.module), + } + + def GetJinjaExportsForInterface(self, interface): + exports = self.GetJinjaExports() + exports.update({'interface': interface}) + return exports + + @UseJinja('java_templates/enum.java.tmpl', filters=java_filters) + def GenerateEnumSource(self, enum): + exports = self.GetJinjaExports() + exports.update({'enum': enum}) + return exports + + @UseJinja('java_templates/struct.java.tmpl', filters=java_filters) + def GenerateStructSource(self, struct): + exports = self.GetJinjaExports() + exports.update({'struct': struct}) + return exports + + @UseJinja('java_templates/union.java.tmpl', filters=java_filters) + def GenerateUnionSource(self, union): + exports = self.GetJinjaExports() + exports.update({'union': union}) + return exports + + @UseJinja('java_templates/interface.java.tmpl', filters=java_filters) + def GenerateInterfaceSource(self, interface): + return self.GetJinjaExportsForInterface(interface) + + @UseJinja('java_templates/interface_internal.java.tmpl', filters=java_filters) + def GenerateInterfaceInternalSource(self, interface): + return self.GetJinjaExportsForInterface(interface) + + @UseJinja('java_templates/constants.java.tmpl', filters=java_filters) + def GenerateConstantsSource(self, module): + exports = self.GetJinjaExports() + exports.update({'main_entity': GetConstantsMainEntityName(module), + 'constants': module.constants}) + return exports + + def DoGenerateFiles(self): + fileutil.EnsureDirectoryExists(self.output_dir) + + # Keep this above the others as .GetStructs() changes the state of the + # module, annotating structs with required information. + for struct in self.GetStructs(): + self.Write(self.GenerateStructSource(struct), + '%s.java' % GetNameForElement(struct)) + + for union in self.module.unions: + self.Write(self.GenerateUnionSource(union), + '%s.java' % GetNameForElement(union)) + + for enum in self.module.enums: + self.Write(self.GenerateEnumSource(enum), + '%s.java' % GetNameForElement(enum)) + + for interface in self.GetInterfaces(): + self.Write(self.GenerateInterfaceSource(interface), + '%s.java' % GetNameForElement(interface)) + self.Write(self.GenerateInterfaceInternalSource(interface), + '%s_Internal.java' % GetNameForElement(interface)) + + if self.module.constants: + self.Write(self.GenerateConstantsSource(self.module), + '%s.java' % GetConstantsMainEntityName(self.module)) + + def GenerateFiles(self, unparsed_args): + parser = argparse.ArgumentParser() + parser.add_argument('--java_output_directory', dest='java_output_directory') + args = parser.parse_args(unparsed_args) + package_path = GetPackage(self.module).replace('.', '/') + + # Generate the java files in a temporary directory and place a single + # srcjar in the output directory. + basename = self.MatchMojomFilePath("%s.srcjar" % self.module.name) + zip_filename = os.path.join(self.output_dir, basename) + with TempDir() as temp_java_root: + self.output_dir = os.path.join(temp_java_root, package_path) + self.DoGenerateFiles(); + ZipContentInto(temp_java_root, zip_filename) + + if args.java_output_directory: + # If requested, generate the java files directly into indicated directory. + self.output_dir = os.path.join(args.java_output_directory, package_path) + self.DoGenerateFiles(); + + def GetJinjaParameters(self): + return { + 'lstrip_blocks': True, + 'trim_blocks': True, + } + + def GetGlobals(self): + return { + 'namespace': self.module.namespace, + 'module': self.module, + } diff --git a/mojo/public/tools/bindings/generators/mojom_js_generator.py b/mojo/public/tools/bindings/generators/mojom_js_generator.py new file mode 100644 index 0000000..23c203a --- /dev/null +++ b/mojo/public/tools/bindings/generators/mojom_js_generator.py @@ -0,0 +1,416 @@ +# 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. + +"""Generates JavaScript source files from a mojom.Module.""" + +import mojom.generate.generator as generator +import mojom.generate.module as mojom +import mojom.generate.pack as pack +from mojom.generate.template_expander import UseJinja + +_kind_to_javascript_default_value = { + mojom.BOOL: "false", + mojom.INT8: "0", + mojom.UINT8: "0", + mojom.INT16: "0", + mojom.UINT16: "0", + mojom.INT32: "0", + mojom.UINT32: "0", + mojom.FLOAT: "0", + mojom.HANDLE: "null", + mojom.DCPIPE: "null", + mojom.DPPIPE: "null", + mojom.MSGPIPE: "null", + mojom.SHAREDBUFFER: "null", + mojom.NULLABLE_HANDLE: "null", + mojom.NULLABLE_DCPIPE: "null", + mojom.NULLABLE_DPPIPE: "null", + mojom.NULLABLE_MSGPIPE: "null", + mojom.NULLABLE_SHAREDBUFFER: "null", + mojom.INT64: "0", + mojom.UINT64: "0", + mojom.DOUBLE: "0", + mojom.STRING: "null", + mojom.NULLABLE_STRING: "null" +} + + +def JavaScriptType(kind): + if kind.imported_from: + return kind.imported_from["unique_name"] + "." + kind.name + return kind.name + + +def JavaScriptDefaultValue(field): + if field.default: + if mojom.IsStructKind(field.kind): + assert field.default == "default" + return "new %s()" % JavaScriptType(field.kind) + return ExpressionToText(field.default) + if field.kind in mojom.PRIMITIVES: + return _kind_to_javascript_default_value[field.kind] + if mojom.IsStructKind(field.kind): + return "null" + if mojom.IsUnionKind(field.kind): + return "null" + if mojom.IsArrayKind(field.kind): + return "null" + if mojom.IsMapKind(field.kind): + return "null" + if mojom.IsInterfaceKind(field.kind) or \ + mojom.IsInterfaceRequestKind(field.kind): + return _kind_to_javascript_default_value[mojom.MSGPIPE] + if mojom.IsAssociatedKind(field.kind): + return "null" + if mojom.IsEnumKind(field.kind): + return "0" + raise Exception("No valid default: %s" % field) + + +def JavaScriptPayloadSize(packed): + packed_fields = packed.packed_fields + if not packed_fields: + return 0 + last_field = packed_fields[-1] + offset = last_field.offset + last_field.size + pad = pack.GetPad(offset, 8) + return offset + pad + + +_kind_to_codec_type = { + mojom.BOOL: "codec.Uint8", + mojom.INT8: "codec.Int8", + mojom.UINT8: "codec.Uint8", + mojom.INT16: "codec.Int16", + mojom.UINT16: "codec.Uint16", + mojom.INT32: "codec.Int32", + mojom.UINT32: "codec.Uint32", + mojom.FLOAT: "codec.Float", + mojom.HANDLE: "codec.Handle", + mojom.DCPIPE: "codec.Handle", + mojom.DPPIPE: "codec.Handle", + mojom.MSGPIPE: "codec.Handle", + mojom.SHAREDBUFFER: "codec.Handle", + mojom.NULLABLE_HANDLE: "codec.NullableHandle", + mojom.NULLABLE_DCPIPE: "codec.NullableHandle", + mojom.NULLABLE_DPPIPE: "codec.NullableHandle", + mojom.NULLABLE_MSGPIPE: "codec.NullableHandle", + mojom.NULLABLE_SHAREDBUFFER: "codec.NullableHandle", + mojom.INT64: "codec.Int64", + mojom.UINT64: "codec.Uint64", + mojom.DOUBLE: "codec.Double", + mojom.STRING: "codec.String", + mojom.NULLABLE_STRING: "codec.NullableString", +} + + +def CodecType(kind): + if kind in mojom.PRIMITIVES: + return _kind_to_codec_type[kind] + if mojom.IsStructKind(kind): + pointer_type = "NullablePointerTo" if mojom.IsNullableKind(kind) \ + else "PointerTo" + return "new codec.%s(%s)" % (pointer_type, JavaScriptType(kind)) + if mojom.IsUnionKind(kind): + return JavaScriptType(kind) + if mojom.IsArrayKind(kind): + array_type = "NullableArrayOf" if mojom.IsNullableKind(kind) else "ArrayOf" + array_length = "" if kind.length is None else ", %d" % kind.length + element_type = ElementCodecType(kind.kind) + return "new codec.%s(%s%s)" % (array_type, element_type, array_length) + if mojom.IsInterfaceKind(kind): + return "codec.%s" % ("NullableInterface" if mojom.IsNullableKind(kind) + else "Interface") + if mojom.IsInterfaceRequestKind(kind): + return CodecType(mojom.MSGPIPE) + if mojom.IsAssociatedInterfaceKind(kind): + return "codec.AssociatedInterfaceNotSupported" + if mojom.IsAssociatedInterfaceRequestKind(kind): + return "codec.AssociatedInterfaceRequestNotSupported" + if mojom.IsEnumKind(kind): + return _kind_to_codec_type[mojom.INT32] + if mojom.IsMapKind(kind): + map_type = "NullableMapOf" if mojom.IsNullableKind(kind) else "MapOf" + key_type = ElementCodecType(kind.key_kind) + value_type = ElementCodecType(kind.value_kind) + return "new codec.%s(%s, %s)" % (map_type, key_type, value_type) + raise Exception("No codec type for %s" % kind) + + +def ElementCodecType(kind): + return "codec.PackedBool" if mojom.IsBoolKind(kind) else CodecType(kind) + +def JavaScriptDecodeSnippet(kind): + if (kind in mojom.PRIMITIVES or mojom.IsUnionKind(kind) or + mojom.IsInterfaceKind(kind) or mojom.IsAssociatedKind(kind)): + return "decodeStruct(%s)" % CodecType(kind) + if mojom.IsStructKind(kind): + return "decodeStructPointer(%s)" % JavaScriptType(kind) + if mojom.IsMapKind(kind): + return "decodeMapPointer(%s, %s)" % \ + (ElementCodecType(kind.key_kind), ElementCodecType(kind.value_kind)) + if mojom.IsArrayKind(kind) and mojom.IsBoolKind(kind.kind): + return "decodeArrayPointer(codec.PackedBool)" + if mojom.IsArrayKind(kind): + return "decodeArrayPointer(%s)" % CodecType(kind.kind) + if mojom.IsUnionKind(kind): + return "decodeUnion(%s)" % CodecType(kind) + if mojom.IsInterfaceRequestKind(kind): + return JavaScriptDecodeSnippet(mojom.MSGPIPE) + if mojom.IsEnumKind(kind): + return JavaScriptDecodeSnippet(mojom.INT32) + raise Exception("No decode snippet for %s" % kind) + + +def JavaScriptEncodeSnippet(kind): + if (kind in mojom.PRIMITIVES or mojom.IsUnionKind(kind) or + mojom.IsInterfaceKind(kind) or mojom.IsAssociatedKind(kind)): + return "encodeStruct(%s, " % CodecType(kind) + if mojom.IsUnionKind(kind): + return "encodeStruct(%s, " % JavaScriptType(kind) + if mojom.IsStructKind(kind): + return "encodeStructPointer(%s, " % JavaScriptType(kind) + if mojom.IsMapKind(kind): + return "encodeMapPointer(%s, %s, " % \ + (ElementCodecType(kind.key_kind), ElementCodecType(kind.value_kind)) + if mojom.IsArrayKind(kind) and mojom.IsBoolKind(kind.kind): + return "encodeArrayPointer(codec.PackedBool, "; + if mojom.IsArrayKind(kind): + return "encodeArrayPointer(%s, " % CodecType(kind.kind) + if mojom.IsInterfaceRequestKind(kind): + return JavaScriptEncodeSnippet(mojom.MSGPIPE) + if mojom.IsEnumKind(kind): + return JavaScriptEncodeSnippet(mojom.INT32) + raise Exception("No encode snippet for %s" % kind) + + +def JavaScriptUnionDecodeSnippet(kind): + if mojom.IsUnionKind(kind): + return "decodeStructPointer(%s)" % JavaScriptType(kind) + return JavaScriptDecodeSnippet(kind) + + +def JavaScriptUnionEncodeSnippet(kind): + if mojom.IsUnionKind(kind): + return "encodeStructPointer(%s, " % JavaScriptType(kind) + return JavaScriptEncodeSnippet(kind) + + +def JavaScriptFieldOffset(packed_field): + return "offset + codec.kStructHeaderSize + %s" % packed_field.offset + + +def JavaScriptNullableParam(field): + return "true" if mojom.IsNullableKind(field.kind) else "false" + + +def GetArrayExpectedDimensionSizes(kind): + expected_dimension_sizes = [] + while mojom.IsArrayKind(kind): + expected_dimension_sizes.append(generator.ExpectedArraySize(kind) or 0) + kind = kind.kind + # Strings are serialized as variable-length arrays. + if (mojom.IsStringKind(kind)): + expected_dimension_sizes.append(0) + return expected_dimension_sizes + + +def JavaScriptValidateArrayParams(field): + nullable = JavaScriptNullableParam(field) + element_kind = field.kind.kind + element_size = pack.PackedField.GetSizeForKind(element_kind) + expected_dimension_sizes = GetArrayExpectedDimensionSizes( + field.kind) + element_type = ElementCodecType(element_kind) + return "%s, %s, %s, %s, 0" % \ + (element_size, element_type, nullable, + expected_dimension_sizes) + + +def JavaScriptValidateStructParams(field): + nullable = JavaScriptNullableParam(field) + struct_type = JavaScriptType(field.kind) + return "%s, %s" % (struct_type, nullable) + +def JavaScriptValidateUnionParams(field): + nullable = JavaScriptNullableParam(field) + union_type = JavaScriptType(field.kind) + return "%s, %s" % (union_type, nullable) + +def JavaScriptValidateMapParams(field): + nullable = JavaScriptNullableParam(field) + keys_type = ElementCodecType(field.kind.key_kind) + values_kind = field.kind.value_kind; + values_type = ElementCodecType(values_kind) + values_nullable = "true" if mojom.IsNullableKind(values_kind) else "false" + return "%s, %s, %s, %s" % \ + (nullable, keys_type, values_type, values_nullable) + + +def JavaScriptValidateStringParams(field): + nullable = JavaScriptNullableParam(field) + return "%s" % (nullable) + + +def JavaScriptValidateHandleParams(field): + nullable = JavaScriptNullableParam(field) + return "%s" % (nullable) + +def JavaScriptValidateInterfaceParams(field): + return JavaScriptValidateHandleParams(field) + +def JavaScriptProxyMethodParameterValue(parameter): + name = parameter.name; + if (mojom.IsInterfaceKind(parameter.kind)): + type = JavaScriptType(parameter.kind) + return "core.isHandle(%s) ? %s : connection.bindImpl" \ + "(%s, %s)" % (name, name, name, type) + if (mojom.IsInterfaceRequestKind(parameter.kind)): + type = JavaScriptType(parameter.kind.kind) + return "core.isHandle(%s) ? %s : connection.bindProxy" \ + "(%s, %s)" % (name, name, name, type) + return name; + + +def JavaScriptStubMethodParameterValue(parameter): + name = parameter.name; + if (mojom.IsInterfaceKind(parameter.kind)): + type = JavaScriptType(parameter.kind) + return "connection.bindHandleToProxy(%s, %s)" % (name, type) + if (mojom.IsInterfaceRequestKind(parameter.kind)): + type = JavaScriptType(parameter.kind.kind) + return "connection.bindHandleToStub(%s, %s)" % (name, type) + return name; + + +def TranslateConstants(token): + if isinstance(token, (mojom.EnumValue, mojom.NamedValue)): + # Both variable and enum constants are constructed like: + # NamespaceUid.Struct[.Enum].CONSTANT_NAME + name = [] + if token.imported_from: + name.append(token.imported_from["unique_name"]) + if token.parent_kind: + name.append(token.parent_kind.name) + if isinstance(token, mojom.EnumValue): + name.append(token.enum.name) + name.append(token.name) + return ".".join(name) + + if isinstance(token, mojom.BuiltinValue): + if token.value == "double.INFINITY" or token.value == "float.INFINITY": + return "Infinity"; + if token.value == "double.NEGATIVE_INFINITY" or \ + token.value == "float.NEGATIVE_INFINITY": + return "-Infinity"; + if token.value == "double.NAN" or token.value == "float.NAN": + return "NaN"; + + return token + + +def ExpressionToText(value): + return TranslateConstants(value) + +def IsArrayPointerField(field): + return mojom.IsArrayKind(field.kind) + +def IsStringPointerField(field): + return mojom.IsStringKind(field.kind) + +def IsStructPointerField(field): + return mojom.IsStructKind(field.kind) + +def IsMapPointerField(field): + return mojom.IsMapKind(field.kind) + +def IsHandleField(field): + return mojom.IsAnyHandleKind(field.kind) + +def IsInterfaceField(field): + return mojom.IsInterfaceKind(field.kind) + +def IsUnionField(field): + return mojom.IsUnionKind(field.kind) + + +class Generator(generator.Generator): + + js_filters = { + "default_value": JavaScriptDefaultValue, + "payload_size": JavaScriptPayloadSize, + "decode_snippet": JavaScriptDecodeSnippet, + "encode_snippet": JavaScriptEncodeSnippet, + "union_decode_snippet": JavaScriptUnionDecodeSnippet, + "union_encode_snippet": JavaScriptUnionEncodeSnippet, + "expression_to_text": ExpressionToText, + "field_offset": JavaScriptFieldOffset, + "has_callbacks": mojom.HasCallbacks, + "is_array_pointer_field": IsArrayPointerField, + "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, + "js_type": JavaScriptType, + "js_proxy_method_parameter_value": JavaScriptProxyMethodParameterValue, + "js_stub_method_parameter_value": JavaScriptStubMethodParameterValue, + "stylize_method": generator.StudlyCapsToCamel, + "validate_array_params": JavaScriptValidateArrayParams, + "validate_handle_params": JavaScriptValidateHandleParams, + "validate_interface_params": JavaScriptValidateInterfaceParams, + "validate_map_params": JavaScriptValidateMapParams, + "validate_string_params": JavaScriptValidateStringParams, + "validate_struct_params": JavaScriptValidateStructParams, + "validate_union_params": JavaScriptValidateUnionParams, + } + + def GetParameters(self): + return { + "namespace": self.module.namespace, + "imports": self.GetImports(), + "kinds": self.module.kinds, + "enums": self.module.enums, + "module": self.module, + "structs": self.GetStructs() + self.GetStructsFromMethods(), + "unions": self.GetUnions(), + "interfaces": self.GetInterfaces(), + "imported_interfaces": self.GetImportedInterfaces(), + } + + @UseJinja("js_templates/module.amd.tmpl", filters=js_filters) + def GenerateAMDModule(self): + return self.GetParameters() + + def GenerateFiles(self, args): + self.Write(self.GenerateAMDModule(), + self.MatchMojomFilePath("%s.js" % self.module.name)) + + def GetImports(self): + used_names = set() + for each_import in self.module.imports: + simple_name = each_import["module_name"].split(".")[0] + + # Since each import is assigned a variable in JS, they need to have unique + # names. + unique_name = simple_name + counter = 0 + while unique_name in used_names: + counter += 1 + unique_name = simple_name + str(counter) + + used_names.add(unique_name) + each_import["unique_name"] = unique_name + "$" + counter += 1 + return self.module.imports + + def GetImportedInterfaces(self): + interface_to_import = {}; + for each_import in self.module.imports: + for each_interface in each_import["module"].interfaces: + name = each_interface.name + interface_to_import[name] = each_import["unique_name"] + "." + name + return interface_to_import; + diff --git a/mojo/public/tools/bindings/generators/run_cpp_generator.py b/mojo/public/tools/bindings/generators/run_cpp_generator.py new file mode 100755 index 0000000..4c6f597 --- /dev/null +++ b/mojo/public/tools/bindings/generators/run_cpp_generator.py @@ -0,0 +1,28 @@ +#!/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/mojo/public/tools/bindings/mojom.gni b/mojo/public/tools/bindings/mojom.gni new file mode 100644 index 0000000..7143ef8 --- /dev/null +++ b/mojo/public/tools/bindings/mojom.gni @@ -0,0 +1,314 @@ +# 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. + +import("../../mojo_sdk.gni") + +# Generate C++/JavaScript/Java source files from mojom files. The output files +# will go under the generated file directory tree with the same path as each +# input file. +# +# If a mojom target is intended for use in a client repo where the location of +# the Mojo SDK will be different than its location in the Mojo repo, +# dependencies on the SDK should be specified relative to the parent directory +# of the Mojo public SDK in |mojo_sdk_deps| rather than via absolute paths in +# |deps|. +# +# Parameters: +# +# sources (optional if one of the deps sets listed below is present) +# List of source .mojom files to compile. +# +# deps (optional) +# Note: this can contain only other mojom targets. +# +# mojo_sdk_deps (optional) +# List of deps specified relative to the parent directory of the Mojo +# public SDK. These deps will be added as ordinary deps rebased to the +# current directory. +# +# public_deps (optional) +# Note: this can contain only other mojom targets. +# +# mojo_sdk_public_deps (optional) +# List of public deps specified relative to the parent directory of the +# Mojo public SDK. These deps will be added as ordinary public deps +# rebased to the current directory. +# +# import_dirs (optional) +# List of import directories that will get added when processing sources. +# +# with_environment (optional) +# Set to |false| to omit an implicit bindings dependency on the Chromium +# Mojo environment implementation. Defaults to |true| and in general +# should only be overridden by mojom targets within the Mojo EDK. +# +# testonly (optional) +# +# visibility (optional) +template("mojom") { + assert( + defined(invoker.sources) || defined(invoker.deps) || + defined(invoker.public_deps) || defined(invoker.mojo_sdk_deps) || + defined(invoker.mojo_sdk_public_deps), + "\"sources\" or \"deps\" must be defined for the $target_name template.") + + cpp_sources_suffix = "cpp_sources" + cpp_sources_target_name = "${target_name}_${cpp_sources_suffix}" + if (defined(invoker.sources)) { + generator_root = "//mojo/public/tools/bindings" + generator_script = "$generator_root/mojom_bindings_generator.py" + generator_sources = [ + generator_script, + "$generator_root/generators/cpp_templates/enum_macros.tmpl", + "$generator_root/generators/cpp_templates/interface_declaration.tmpl", + "$generator_root/generators/cpp_templates/interface_definition.tmpl", + "$generator_root/generators/cpp_templates/interface_macros.tmpl", + "$generator_root/generators/cpp_templates/interface_proxy_declaration.tmpl", + "$generator_root/generators/cpp_templates/interface_request_validator_declaration.tmpl", + "$generator_root/generators/cpp_templates/interface_response_validator_declaration.tmpl", + "$generator_root/generators/cpp_templates/interface_stub_declaration.tmpl", + "$generator_root/generators/cpp_templates/module-internal.h.tmpl", + "$generator_root/generators/cpp_templates/module.cc.tmpl", + "$generator_root/generators/cpp_templates/module.h.tmpl", + "$generator_root/generators/cpp_templates/struct_declaration.tmpl", + "$generator_root/generators/cpp_templates/struct_definition.tmpl", + "$generator_root/generators/cpp_templates/struct_macros.tmpl", + "$generator_root/generators/cpp_templates/struct_serialization_declaration.tmpl", + "$generator_root/generators/cpp_templates/struct_serialization_definition.tmpl", + "$generator_root/generators/cpp_templates/union_declaration.tmpl", + "$generator_root/generators/cpp_templates/union_definition.tmpl", + "$generator_root/generators/cpp_templates/union_serialization_declaration.tmpl", + "$generator_root/generators/cpp_templates/union_serialization_definition.tmpl", + "$generator_root/generators/cpp_templates/validation_macros.tmpl", + "$generator_root/generators/cpp_templates/wrapper_class_declaration.tmpl", + "$generator_root/generators/cpp_templates/wrapper_class_definition.tmpl", + "$generator_root/generators/cpp_templates/wrapper_union_class_declaration.tmpl", + "$generator_root/generators/cpp_templates/wrapper_union_class_definition.tmpl", + "$generator_root/generators/java_templates/constant_definition.tmpl", + "$generator_root/generators/java_templates/constants.java.tmpl", + "$generator_root/generators/java_templates/data_types_definition.tmpl", + "$generator_root/generators/java_templates/enum_definition.tmpl", + "$generator_root/generators/java_templates/enum.java.tmpl", + "$generator_root/generators/java_templates/header.java.tmpl", + "$generator_root/generators/java_templates/interface_definition.tmpl", + "$generator_root/generators/java_templates/interface_internal.java.tmpl", + "$generator_root/generators/java_templates/interface.java.tmpl", + "$generator_root/generators/java_templates/struct.java.tmpl", + "$generator_root/generators/java_templates/union.java.tmpl", + "$generator_root/generators/js_templates/enum_definition.tmpl", + "$generator_root/generators/js_templates/interface_definition.tmpl", + "$generator_root/generators/js_templates/module.amd.tmpl", + "$generator_root/generators/js_templates/module_definition.tmpl", + "$generator_root/generators/js_templates/struct_definition.tmpl", + "$generator_root/generators/js_templates/union_definition.tmpl", + "$generator_root/generators/js_templates/validation_macros.tmpl", + "$generator_root/generators/mojom_cpp_generator.py", + "$generator_root/generators/mojom_js_generator.py", + "$generator_root/generators/mojom_java_generator.py", + "$generator_root/pylib/mojom/__init__.py", + "$generator_root/pylib/mojom/error.py", + "$generator_root/pylib/mojom/generate/__init__.py", + "$generator_root/pylib/mojom/generate/constant_resolver.py", + "$generator_root/pylib/mojom/generate/data.py", + "$generator_root/pylib/mojom/generate/generator.py", + "$generator_root/pylib/mojom/generate/module.py", + "$generator_root/pylib/mojom/generate/pack.py", + "$generator_root/pylib/mojom/generate/template_expander.py", + "$generator_root/pylib/mojom/parse/__init__.py", + "$generator_root/pylib/mojom/parse/ast.py", + "$generator_root/pylib/mojom/parse/lexer.py", + "$generator_root/pylib/mojom/parse/parser.py", + "$generator_root/pylib/mojom/parse/translate.py", + ] + generator_cpp_outputs = [ + "{{source_gen_dir}}/{{source_name_part}}.mojom.cc", + "{{source_gen_dir}}/{{source_name_part}}.mojom.h", + "{{source_gen_dir}}/{{source_name_part}}.mojom-internal.h", + ] + generator_js_outputs = + [ "{{source_gen_dir}}/{{source_name_part}}.mojom.js" ] + generator_java_outputs = + [ "{{source_gen_dir}}/{{source_name_part}}.mojom.srcjar" ] + } + + rebased_mojo_sdk_public_deps = [] + if (defined(invoker.mojo_sdk_public_deps)) { + foreach(sdk_dep, invoker.mojo_sdk_public_deps) { + # Check that the SDK dep was not mistakenly given as an absolute path. + assert(get_path_info(sdk_dep, "abspath") != sdk_dep) + rebased_mojo_sdk_public_deps += [ rebase_path(sdk_dep, ".", "//") ] + } + } + + rebased_mojo_sdk_deps = [] + if (defined(invoker.mojo_sdk_deps)) { + foreach(sdk_dep, invoker.mojo_sdk_deps) { + # Check that the SDK dep was not mistakenly given as an absolute path. + assert(get_path_info(sdk_dep, "abspath") != sdk_dep) + rebased_mojo_sdk_deps += [ rebase_path(sdk_dep, ".", "//") ] + } + } + + if (defined(invoker.sources)) { + generator_target_name = target_name + "__generator" + action_foreach(generator_target_name) { + script = generator_script + inputs = generator_sources + sources = invoker.sources + outputs = + generator_cpp_outputs + generator_java_outputs + generator_js_outputs + args = [ + "{{source}}", + "--use_bundled_pylibs", + "-d", + rebase_path("//", root_build_dir), + "-I", + rebase_path("//", root_build_dir), + "-o", + rebase_path(root_gen_dir), + ] + + if (defined(invoker.import_dirs)) { + foreach(import_dir, invoker.import_dirs) { + args += [ + "-I", + rebase_path(import_dir, root_build_dir), + ] + } + } + } + } + + source_set(target_name) { + if (defined(invoker.visibility)) { + visibility = invoker.visibility + } + if (defined(invoker.testonly)) { + testonly = invoker.testonly + } + if (defined(invoker.sources)) { + data = process_file_template(invoker.sources, generator_js_outputs) + } + + public_deps = [ + "//mojo/public/cpp/bindings", + ] + if (defined(invoker.sources)) { + public_deps += [ ":${cpp_sources_target_name}" ] + } + public_deps += rebased_mojo_sdk_public_deps + if (defined(invoker.public_deps)) { + public_deps += invoker.public_deps + } + + deps = [] + if (defined(invoker.sources)) { + public_deps += [ ":$generator_target_name" ] + } + deps += rebased_mojo_sdk_deps + if (defined(invoker.deps)) { + deps += invoker.deps + } + if (defined(invoker.mojo_sdk_deps)) { + foreach(sdk_dep, invoker.mojo_sdk_deps) { + # Check that the SDK dep was not mistakenly given as an absolute path. + assert(get_path_info(sdk_dep, "abspath") != sdk_dep) + deps += [ rebase_path(sdk_dep, ".", "//") ] + } + } + } + + all_deps = rebased_mojo_sdk_deps + rebased_mojo_sdk_public_deps + if (defined(invoker.deps)) { + all_deps += invoker.deps + } + if (defined(invoker.public_deps)) { + all_deps += invoker.public_deps + } + + group("${target_name}__is_mojom") { + } + + # Explicitly ensure that all dependencies (invoker.deps and + # invoker.public_deps) are mojom targets themselves. + group("${target_name}__check_deps_are_all_mojom") { + deps = [] + foreach(d, all_deps) { + name = get_label_info(d, "label_no_toolchain") + toolchain = get_label_info(d, "toolchain") + deps += [ "${name}__is_mojom(${toolchain})" ] + } + } + + if (defined(invoker.sources)) { + # The generated C++ source files. The main reason to introduce this target + # is so that mojo/public/cpp/bindings can depend on mojom interfaces without + # circular dependencies. It means that the target is missing the dependency + # on mojo/public/cpp/bindings. No external targets should depend directly on + # this target *except* mojo/public/cpp/bindings and other *_cpp_sources + # targets. + source_set(cpp_sources_target_name) { + if (defined(invoker.testonly)) { + testonly = invoker.testonly + } + sources = process_file_template(invoker.sources, generator_cpp_outputs) + deps = [ + ":$generator_target_name", + "//base", + "//mojo/public/interfaces/bindings:bindings__generator", + ] + foreach(d, all_deps) { + # Resolve the name, so that a target //mojo/something becomes + # //mojo/something:something and we can append cpp_sources_suffix to + # get the cpp dependency name. + full_name = get_label_info(d, "label_no_toolchain") + deps += [ "${full_name}_${cpp_sources_suffix}" ] + } + if (!defined(invoker.with_environment) || invoker.with_environment) { + deps += [ "//mojo/environment:chromium" ] + } + } + } + + if (is_android) { + import("//build/config/android/rules.gni") + + java_srcjar_target_name = target_name + "_java_sources" + action(java_srcjar_target_name) { + script = "//mojo/public/tools/gn/zip.py" + inputs = process_file_template(invoker.sources, generator_java_outputs) + output = "$target_gen_dir/$target_name.srcjar" + outputs = [ + output, + ] + rebase_inputs = rebase_path(inputs, root_build_dir) + rebase_output = rebase_path(output, root_build_dir) + args = [ + "--zip-inputs=$rebase_inputs", + "--output=$rebase_output", + ] + deps = [ + ":$generator_target_name", + ] + } + + java_target_name = target_name + "_java" + android_library(java_target_name) { + deps = [ + "//mojo/public/java:bindings", + "//mojo/public/java:system", + ] + + foreach(d, all_deps) { + # Resolve the name, so that a target //mojo/something becomes + # //mojo/something:something and we can append "_java" to get the java + # dependency name. + full_name = get_label_info(d, "label_no_toolchain") + deps += [ "${full_name}_java" ] + } + + srcjar_deps = [ ":$java_srcjar_target_name" ] + } + } +} diff --git a/mojo/public/tools/bindings/mojom_bindings_generator.py b/mojo/public/tools/bindings/mojom_bindings_generator.py new file mode 100755 index 0000000..17a3dbe --- /dev/null +++ b/mojo/public/tools/bindings/mojom_bindings_generator.py @@ -0,0 +1,212 @@ +#!/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. + +"""The frontend for the Mojo bindings system.""" + + +import argparse +import imp +import os +import pprint +import sys + +# Disable lint check for finding modules: +# pylint: disable=F0401 + +def _GetDirAbove(dirname): + """Returns the directory "above" this file containing |dirname| (which must + also be "above" this file).""" + path = os.path.abspath(__file__) + while True: + path, tail = os.path.split(path) + assert tail + if tail == dirname: + return path + +# Manually check for the command-line flag. (This isn't quite right, since it +# ignores, e.g., "--", but it's close enough.) +if "--use_bundled_pylibs" in sys.argv[1:]: + sys.path.insert(0, os.path.join(_GetDirAbove("public"), "public/third_party")) + +sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), + "pylib")) + +from mojom.error import Error +import mojom.fileutil as fileutil +from mojom.generate.data import OrderedModuleFromData +from mojom.parse.parser import Parse +from mojom.parse.translate import Translate + + +def LoadGenerators(generators_string): + if not generators_string: + return [] # No generators. + + script_dir = os.path.dirname(os.path.abspath(__file__)) + generators = [] + for generator_name in [s.strip() for s in generators_string.split(",")]: + # "Built-in" generators: + if generator_name.lower() == "c++": + generator_name = os.path.join(script_dir, "generators", + "mojom_cpp_generator.py") + elif generator_name.lower() == "javascript": + generator_name = os.path.join(script_dir, "generators", + "mojom_js_generator.py") + elif generator_name.lower() == "java": + generator_name = os.path.join(script_dir, "generators", + "mojom_java_generator.py") + # Specified generator python module: + elif generator_name.endswith(".py"): + pass + else: + print "Unknown generator name %s" % generator_name + sys.exit(1) + generator_module = imp.load_source(os.path.basename(generator_name)[:-3], + generator_name) + generators.append(generator_module) + return generators + + +def MakeImportStackMessage(imported_filename_stack): + """Make a (human-readable) message listing a chain of imports. (Returned + string begins with a newline (if nonempty) and does not end with one.)""" + return ''.join( + reversed(["\n %s was imported by %s" % (a, b) for (a, b) in \ + zip(imported_filename_stack[1:], imported_filename_stack)])) + + +def FindImportFile(dir_name, file_name, search_dirs): + for search_dir in [dir_name] + search_dirs: + path = os.path.join(search_dir, file_name) + if os.path.isfile(path): + return path + return os.path.join(dir_name, file_name) + +class MojomProcessor(object): + def __init__(self, should_generate): + self._should_generate = should_generate + self._processed_files = {} + self._parsed_files = {} + + def ProcessFile(self, args, remaining_args, generator_modules, filename): + self._ParseFileAndImports(filename, args.import_directories, []) + + return self._GenerateModule(args, remaining_args, generator_modules, + filename) + + def _GenerateModule(self, args, remaining_args, generator_modules, filename): + # Return the already-generated module. + if filename in self._processed_files: + return self._processed_files[filename] + tree = self._parsed_files[filename] + + dirname, name = os.path.split(filename) + 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']: + import_filename = FindImportFile(dirname, + import_data['filename'], + args.import_directories) + import_data['module'] = self._GenerateModule( + args, remaining_args, generator_modules, import_filename) + + module = OrderedModuleFromData(mojom) + + # Set the path as relative to the source root. + module.path = os.path.relpath(os.path.abspath(filename), + os.path.abspath(args.depth)) + + # Normalize to unix-style path here to keep the generators simpler. + module.path = module.path.replace('\\', '/') + + if self._should_generate(filename): + for generator_module in generator_modules: + generator = generator_module.Generator(module, args.output_dir) + filtered_args = [] + if hasattr(generator_module, 'GENERATOR_PREFIX'): + prefix = '--' + generator_module.GENERATOR_PREFIX + '_' + filtered_args = [arg for arg in remaining_args + if arg.startswith(prefix)] + generator.GenerateFiles(filtered_args) + + # Save result. + self._processed_files[filename] = module + return module + + def _ParseFileAndImports(self, filename, import_directories, + imported_filename_stack): + # Ignore already-parsed files. + if filename in self._parsed_files: + return + + if filename in imported_filename_stack: + print "%s: Error: Circular dependency" % filename + \ + MakeImportStackMessage(imported_filename_stack + [filename]) + sys.exit(1) + + try: + with open(filename) as f: + source = f.read() + except IOError as e: + print "%s: Error: %s" % (e.filename, e.strerror) + \ + MakeImportStackMessage(imported_filename_stack + [filename]) + sys.exit(1) + + try: + tree = Parse(source, filename) + except Error as e: + full_stack = imported_filename_stack + [filename] + print str(e) + MakeImportStackMessage(full_stack) + sys.exit(1) + + dirname = os.path.split(filename)[0] + for imp_entry in tree.import_list: + import_filename = FindImportFile(dirname, + imp_entry.import_filename, import_directories) + self._ParseFileAndImports(import_filename, import_directories, + imported_filename_stack + [filename]) + + self._parsed_files[filename] = tree + + +def main(): + parser = argparse.ArgumentParser( + description="Generate bindings from mojom files.") + parser.add_argument("filename", nargs="+", + help="mojom input file") + parser.add_argument("-d", "--depth", dest="depth", default=".", + help="depth from source root") + parser.add_argument("-o", "--output_dir", dest="output_dir", default=".", + help="output directory for generated files") + parser.add_argument("-g", "--generators", dest="generators_string", + metavar="GENERATORS", + default="c++,javascript,java", + help="comma-separated list of generators") + parser.add_argument("--debug_print_intermediate", action="store_true", + help="print the intermediate representation") + parser.add_argument("-I", dest="import_directories", action="append", + metavar="directory", default=[], + help="add a directory to be searched for import files") + parser.add_argument("--use_bundled_pylibs", action="store_true", + help="use Python modules bundled in the SDK") + (args, remaining_args) = parser.parse_known_args() + + generator_modules = LoadGenerators(args.generators_string) + + fileutil.EnsureDirectoryExists(args.output_dir) + + processor = MojomProcessor(lambda filename: filename in args.filename) + for filename in args.filename: + processor.ProcessFile(args, remaining_args, generator_modules, filename) + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/mojo/public/tools/bindings/mojom_bindings_generator_unittest.py b/mojo/public/tools/bindings/mojom_bindings_generator_unittest.py new file mode 100644 index 0000000..de38856 --- /dev/null +++ b/mojo/public/tools/bindings/mojom_bindings_generator_unittest.py @@ -0,0 +1,23 @@ +# 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. + +import unittest + +from mojom_bindings_generator import MakeImportStackMessage + + +class MojoBindingsGeneratorTest(unittest.TestCase): + """Tests mojo_bindings_generator.""" + + def testMakeImportStackMessage(self): + """Tests MakeImportStackMessage().""" + self.assertEquals(MakeImportStackMessage(["x"]), "") + self.assertEquals(MakeImportStackMessage(["x", "y"]), + "\n y was imported by x") + self.assertEquals(MakeImportStackMessage(["x", "y", "z"]), + "\n z was imported by y\n y was imported by x") + + +if __name__ == "__main__": + unittest.main() diff --git a/mojo/public/tools/bindings/mojom_list_outputs.py b/mojo/public/tools/bindings/mojom_list_outputs.py new file mode 100755 index 0000000..ed8bdfa --- /dev/null +++ b/mojo/public/tools/bindings/mojom_list_outputs.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python +# 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. + +import argparse +import os.path +import sys + +def main(): + parser = argparse.ArgumentParser( + description="GYP helper script for mapping mojoms => generated outputs.") + parser.add_argument("--basedir", required=True) + parser.add_argument("mojom", nargs="*") + + args = parser.parse_args() + + for mojom in args.mojom: + full = os.path.join("<(SHARED_INTERMEDIATE_DIR)", args.basedir, mojom) + base, ext = os.path.splitext(full) + assert ext == ".mojom", mojom + # Fix filename escaping issues on Windows. + base = base.replace("\\", "/") + print base + ".mojom.cc" + print base + ".mojom.h" + print base + ".mojom-internal.h" + print base + ".mojom.js" + + return 0 + +if __name__ == "__main__": + sys.exit(main()) diff --git a/mojo/public/tools/bindings/pylib/mojom/__init__.py b/mojo/public/tools/bindings/pylib/mojom/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom/__init__.py diff --git a/mojo/public/tools/bindings/pylib/mojom/error.py b/mojo/public/tools/bindings/pylib/mojom/error.py new file mode 100644 index 0000000..99522b9 --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom/error.py @@ -0,0 +1,27 @@ +# 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. + +class Error(Exception): + """Base class for Mojo IDL bindings parser/generator errors.""" + + def __init__(self, filename, message, lineno=None, addenda=None, **kwargs): + """|filename| is the (primary) file which caused the error, |message| is the + error message, |lineno| is the 1-based line number (or |None| if not + applicable/available), and |addenda| is a list of additional lines to append + to the final error message.""" + Exception.__init__(self, **kwargs) + self.filename = filename + self.message = message + self.lineno = lineno + self.addenda = addenda + + def __str__(self): + if self.lineno: + s = "%s:%d: Error: %s" % (self.filename, self.lineno, self.message) + else: + s = "%s: Error: %s" % (self.filename, self.message) + return "\n".join([s] + self.addenda) if self.addenda else s + + def __repr__(self): + return str(self) diff --git a/mojo/public/tools/bindings/pylib/mojom/fileutil.py b/mojo/public/tools/bindings/pylib/mojom/fileutil.py new file mode 100644 index 0000000..b321e9f --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom/fileutil.py @@ -0,0 +1,18 @@ +# Copyright 2015 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 errno +import os.path + +def EnsureDirectoryExists(path, always_try_to_create=False): + """A wrapper for os.makedirs that does not error if the directory already + exists. A different process could be racing to create this directory.""" + + if not os.path.exists(path) or always_try_to_create: + try: + os.makedirs(path) + except OSError as e: + # There may have been a race to create this directory. + if e.errno != errno.EEXIST: + raise diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/__init__.py b/mojo/public/tools/bindings/pylib/mojom/generate/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom/generate/__init__.py diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/constant_resolver.py b/mojo/public/tools/bindings/pylib/mojom/generate/constant_resolver.py new file mode 100644 index 0000000..c8b21f2 --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom/generate/constant_resolver.py @@ -0,0 +1,91 @@ +# Copyright 2015 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. + +"""Resolves the values used for constants and enums.""" + +from itertools import ifilter +import mojom.generate.module as mojom + +def ResolveConstants(module, expression_to_text): + in_progress = set() + computed = set() + + def GetResolvedValue(named_value): + assert isinstance(named_value, (mojom.EnumValue, mojom.ConstantValue)) + if isinstance(named_value, mojom.EnumValue): + field = next(ifilter(lambda field: field.name == named_value.name, + named_value.enum.fields), None) + if not field: + raise RuntimeError( + 'Unable to get computed value for field %s of enum %s' % + (named_value.name, named_value.enum.name)) + if field not in computed: + ResolveEnum(named_value.enum) + return field.resolved_value + else: + ResolveConstant(named_value.constant) + named_value.resolved_value = named_value.constant.resolved_value + return named_value.resolved_value + + def ResolveConstant(constant): + if constant in computed: + return + if constant in in_progress: + raise RuntimeError('Circular dependency for constant: %s' % constant.name) + in_progress.add(constant) + if isinstance(constant.value, (mojom.EnumValue, mojom.ConstantValue)): + resolved_value = GetResolvedValue(constant.value) + else: + resolved_value = expression_to_text(constant.value) + constant.resolved_value = resolved_value + in_progress.remove(constant) + computed.add(constant) + + def ResolveEnum(enum): + def ResolveEnumField(enum, field, default_value): + if field in computed: + return + if field in in_progress: + raise RuntimeError('Circular dependency for enum: %s' % enum.name) + in_progress.add(field) + if field.value: + if isinstance(field.value, mojom.EnumValue): + resolved_value = GetResolvedValue(field.value) + elif isinstance(field.value, str): + resolved_value = int(field.value, 0) + else: + raise RuntimeError('Unexpected value: %s' % field.value) + else: + resolved_value = default_value + field.resolved_value = resolved_value + in_progress.remove(field) + computed.add(field) + + current_value = 0 + for field in enum.fields: + ResolveEnumField(enum, field, current_value) + current_value = field.resolved_value + 1 + + for constant in module.constants: + ResolveConstant(constant) + + for enum in module.enums: + ResolveEnum(enum) + + for struct in module.structs: + for constant in struct.constants: + ResolveConstant(constant) + for enum in struct.enums: + ResolveEnum(enum) + for field in struct.fields: + if isinstance(field.default, (mojom.ConstantValue, mojom.EnumValue)): + field.default.resolved_value = GetResolvedValue(field.default) + + for interface in module.interfaces: + for constant in interface.constants: + ResolveConstant(constant) + for enum in interface.enums: + ResolveEnum(enum) + + return module diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/data.py b/mojo/public/tools/bindings/pylib/mojom/generate/data.py new file mode 100644 index 0000000..1109a6a --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom/generate/data.py @@ -0,0 +1,488 @@ +# 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.spec = 'x:' + module.namespace + '.' + struct.name + module.kinds[struct.spec] = struct + 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') + 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') + 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'] + 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.fields = map( + lambda field: EnumFieldFromData(module, enum, field, parent_kind), + data['fields']) + enum.attributes = data.get('attributes') + ResolveNumericEnumValues(enum.fields) + + module.kinds[enum.spec] = enum + 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): + 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/mojo/public/tools/bindings/pylib/mojom/generate/data_tests.py b/mojo/public/tools/bindings/pylib/mojom/generate/data_tests.py new file mode 100644 index 0000000..096554c --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom/generate/data_tests.py @@ -0,0 +1,86 @@ +# 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/mojo/public/tools/bindings/pylib/mojom/generate/generator.py b/mojo/public/tools/bindings/pylib/mojom/generate/generator.py new file mode 100644 index 0000000..666ef43 --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom/generate/generator.py @@ -0,0 +1,145 @@ +# 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. + +"""Code shared by the various language-specific code generators.""" + +from functools import partial +import os.path +import re + +import module as mojom +import mojom.fileutil as fileutil +import pack + +def ExpectedArraySize(kind): + if mojom.IsArrayKind(kind): + return kind.length + return None + +def StudlyCapsToCamel(studly): + return studly[0].lower() + studly[1:] + +def CamelCaseToAllCaps(camel_case): + return '_'.join( + word for word in re.split(r'([A-Z][^A-Z]+)', camel_case) if word).upper() + +def UnderToCamel(under): + """Converts underscore_separated strings to CamelCase strings.""" + return ''.join(word.capitalize() for word in under.split('_')) + +def WriteFile(contents, full_path): + # Make sure the containing directory exists. + full_dir = os.path.dirname(full_path) + fileutil.EnsureDirectoryExists(full_dir) + + # Dump the data to disk. + with open(full_path, "w+") as f: + f.write(contents) + +class Generator(object): + # Pass |output_dir| to emit files to disk. Omit |output_dir| to echo all + # files to stdout. + def __init__(self, module, output_dir=None): + self.module = module + self.output_dir = output_dir + + def GetStructsFromMethods(self): + result = [] + for interface in self.module.interfaces: + for method in interface.methods: + result.append(self._GetStructFromMethod(method)) + if method.response_parameters != None: + result.append(self._GetResponseStructFromMethod(method)) + return result + + def GetStructs(self): + return map(partial(self._AddStructComputedData, True), self.module.structs) + + def GetUnions(self): + return map(self._AddUnionComputedData, self.module.unions) + + def GetInterfaces(self): + return map(self._AddInterfaceComputedData, self.module.interfaces) + + # Prepend the filename with a directory that matches the directory of the + # original .mojom file, relative to the import root. + def MatchMojomFilePath(self, filename): + return os.path.join(os.path.dirname(self.module.path), filename) + + def Write(self, contents, filename): + if self.output_dir is None: + print contents + return + full_path = os.path.join(self.output_dir, filename) + WriteFile(contents, full_path) + + def GenerateFiles(self, args): + raise NotImplementedError("Subclasses must override/implement this method") + + def GetJinjaParameters(self): + """Returns default constructor parameters for the jinja environment.""" + return {} + + def GetGlobals(self): + """Returns global mappings for the template generation.""" + return {} + + def _AddStructComputedData(self, exported, struct): + """Adds computed data to the given struct. The data is computed once and + used repeatedly in the generation process.""" + struct.packed = pack.PackedStruct(struct) + struct.bytes = pack.GetByteLayout(struct.packed) + struct.versions = pack.GetVersionInfo(struct.packed) + struct.exported = exported + return struct + + def _AddUnionComputedData(self, union): + """Adds computed data to the given union. The data is computed once and + used repeatedly in the generation process.""" + ordinal = 0 + for field in union.fields: + if field.ordinal is not None: + ordinal = field.ordinal + field.ordinal = ordinal + ordinal += 1 + return union + + def _AddInterfaceComputedData(self, interface): + """Adds computed data to the given interface. The data is computed once and + used repeatedly in the generation process.""" + interface.version = 0 + for method in interface.methods: + if method.min_version is not None: + interface.version = max(interface.version, method.min_version) + + method.param_struct = self._GetStructFromMethod(method) + interface.version = max(interface.version, + method.param_struct.versions[-1].version) + + if method.response_parameters is not None: + method.response_param_struct = self._GetResponseStructFromMethod(method) + interface.version = max( + interface.version, + method.response_param_struct.versions[-1].version) + else: + method.response_param_struct = None + return interface + + def _GetStructFromMethod(self, method): + """Converts a method's parameters into the fields of a struct.""" + params_class = "%s_%s_Params" % (method.interface.name, method.name) + struct = mojom.Struct(params_class, module=method.interface.module) + for param in method.parameters: + struct.AddField(param.name, param.kind, param.ordinal, + attributes=param.attributes) + return self._AddStructComputedData(False, struct) + + def _GetResponseStructFromMethod(self, method): + """Converts a method's response_parameters into the fields of a struct.""" + params_class = "%s_%s_ResponseParams" % (method.interface.name, method.name) + struct = mojom.Struct(params_class, module=method.interface.module) + for param in method.response_parameters: + struct.AddField(param.name, param.kind, param.ordinal, + attributes=param.attributes) + return self._AddStructComputedData(False, struct) diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/generator_unittest.py b/mojo/public/tools/bindings/pylib/mojom/generate/generator_unittest.py new file mode 100644 index 0000000..9966b0b --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom/generate/generator_unittest.py @@ -0,0 +1,24 @@ +# Copyright 2015 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 unittest + +import module as mojom +import generator + +class TestGenerator(unittest.TestCase): + + def testGetUnionsAddsOrdinals(self): + module = mojom.Module() + union = module.AddUnion('a') + union.AddField('a', mojom.BOOL) + union.AddField('b', mojom.BOOL) + union.AddField('c', mojom.BOOL, ordinal=10) + union.AddField('d', mojom.BOOL) + + gen = generator.Generator(module) + union = gen.GetUnions()[0] + ordinals = [field.ordinal for field in union.fields] + + self.assertEquals([0, 1, 10, 11], ordinals) diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/module.py b/mojo/public/tools/bindings/pylib/mojom/generate/module.py new file mode 100644 index 0000000..37c76a2 --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom/generate/module.py @@ -0,0 +1,620 @@ +# 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. + +# This module's classes provide an interface to mojo modules. Modules are +# collections of interfaces and structs to be used by mojo ipc clients and +# servers. +# +# A simple interface would be created this way: +# module = mojom.generate.module.Module('Foo') +# interface = module.AddInterface('Bar') +# method = interface.AddMethod('Tat', 0) +# method.AddParameter('baz', 0, mojom.INT32) + + +class Kind(object): + def __init__(self, spec=None): + self.spec = spec + self.parent_kind = None + + +class ReferenceKind(Kind): + """ReferenceKind represents pointer types and handle types. + A type is nullable if null (for pointer types) or invalid handle (for handle + types) is a legal value for the type. + """ + + def __init__(self, spec=None, is_nullable=False): + assert spec is None or is_nullable == spec.startswith('?') + Kind.__init__(self, spec) + self.is_nullable = is_nullable + self.shared_definition = {} + + def MakeNullableKind(self): + assert not self.is_nullable + + if self == STRING: + return NULLABLE_STRING + if self == HANDLE: + return NULLABLE_HANDLE + if self == DCPIPE: + return NULLABLE_DCPIPE + if self == DPPIPE: + return NULLABLE_DPPIPE + if self == MSGPIPE: + return NULLABLE_MSGPIPE + if self == SHAREDBUFFER: + return NULLABLE_SHAREDBUFFER + + nullable_kind = type(self)() + nullable_kind.shared_definition = self.shared_definition + if self.spec is not None: + nullable_kind.spec = '?' + self.spec + nullable_kind.is_nullable = True + + return nullable_kind + + @classmethod + def AddSharedProperty(cls, name): + """Adds a property |name| to |cls|, which accesses the corresponding item in + |shared_definition|. + + The reason of adding such indirection is to enable sharing definition + between a reference kind and its nullable variation. For example: + a = Struct('test_struct_1') + b = a.MakeNullableKind() + a.name = 'test_struct_2' + print b.name # Outputs 'test_struct_2'. + """ + def Get(self): + return self.shared_definition[name] + + def Set(self, value): + self.shared_definition[name] = value + + setattr(cls, name, property(Get, Set)) + + +# Initialize the set of primitive types. These can be accessed by clients. +BOOL = Kind('b') +INT8 = Kind('i8') +INT16 = Kind('i16') +INT32 = Kind('i32') +INT64 = Kind('i64') +UINT8 = Kind('u8') +UINT16 = Kind('u16') +UINT32 = Kind('u32') +UINT64 = Kind('u64') +FLOAT = Kind('f') +DOUBLE = Kind('d') +STRING = ReferenceKind('s') +HANDLE = ReferenceKind('h') +DCPIPE = ReferenceKind('h:d:c') +DPPIPE = ReferenceKind('h:d:p') +MSGPIPE = ReferenceKind('h:m') +SHAREDBUFFER = ReferenceKind('h:s') +NULLABLE_STRING = ReferenceKind('?s', True) +NULLABLE_HANDLE = ReferenceKind('?h', True) +NULLABLE_DCPIPE = ReferenceKind('?h:d:c', True) +NULLABLE_DPPIPE = ReferenceKind('?h:d:p', True) +NULLABLE_MSGPIPE = ReferenceKind('?h:m', True) +NULLABLE_SHAREDBUFFER = ReferenceKind('?h:s', True) + + +# Collection of all Primitive types +PRIMITIVES = ( + BOOL, + INT8, + INT16, + INT32, + INT64, + UINT8, + UINT16, + UINT32, + UINT64, + FLOAT, + DOUBLE, + STRING, + HANDLE, + DCPIPE, + DPPIPE, + MSGPIPE, + SHAREDBUFFER, + NULLABLE_STRING, + NULLABLE_HANDLE, + NULLABLE_DCPIPE, + NULLABLE_DPPIPE, + NULLABLE_MSGPIPE, + NULLABLE_SHAREDBUFFER +) + + +ATTRIBUTE_MIN_VERSION = 'MinVersion' + + +class NamedValue(object): + def __init__(self, module, parent_kind, name): + self.module = module + self.namespace = module.namespace + self.parent_kind = parent_kind + self.name = name + self.imported_from = None + + def GetSpec(self): + return (self.namespace + '.' + + (self.parent_kind and (self.parent_kind.name + '.') or "") + + self.name) + + +class BuiltinValue(object): + def __init__(self, value): + self.value = value + + +class ConstantValue(NamedValue): + def __init__(self, module, parent_kind, constant): + NamedValue.__init__(self, module, parent_kind, constant.name) + self.constant = constant + + +class EnumValue(NamedValue): + def __init__(self, module, enum, field): + NamedValue.__init__(self, module, enum.parent_kind, field.name) + self.enum = enum + + def GetSpec(self): + return (self.namespace + '.' + + (self.parent_kind and (self.parent_kind.name + '.') or "") + + self.enum.name + '.' + self.name) + + +class Constant(object): + def __init__(self, name=None, kind=None, value=None, parent_kind=None): + self.name = name + self.kind = kind + self.value = value + self.parent_kind = parent_kind + + +class Field(object): + def __init__(self, name=None, kind=None, ordinal=None, default=None, + attributes=None): + if self.__class__.__name__ == 'Field': + raise Exception() + self.name = name + self.kind = kind + self.ordinal = ordinal + self.default = default + self.attributes = attributes + + @property + def min_version(self): + return self.attributes.get(ATTRIBUTE_MIN_VERSION) \ + if self.attributes else None + + +class StructField(Field): pass + + +class UnionField(Field): pass + + +class Struct(ReferenceKind): + ReferenceKind.AddSharedProperty('name') + ReferenceKind.AddSharedProperty('module') + ReferenceKind.AddSharedProperty('imported_from') + ReferenceKind.AddSharedProperty('fields') + ReferenceKind.AddSharedProperty('attributes') + + def __init__(self, name=None, module=None, attributes=None): + if name is not None: + spec = 'x:' + name + else: + spec = None + ReferenceKind.__init__(self, spec) + self.name = name + self.module = module + self.imported_from = None + self.fields = [] + self.attributes = attributes + + def AddField(self, name, kind, ordinal=None, default=None, attributes=None): + field = StructField(name, kind, ordinal, default, attributes) + self.fields.append(field) + return field + + +class Union(ReferenceKind): + ReferenceKind.AddSharedProperty('name') + ReferenceKind.AddSharedProperty('module') + ReferenceKind.AddSharedProperty('imported_from') + ReferenceKind.AddSharedProperty('fields') + ReferenceKind.AddSharedProperty('attributes') + + def __init__(self, name=None, module=None, attributes=None): + if name is not None: + spec = 'x:' + name + else: + spec = None + ReferenceKind.__init__(self, spec) + self.name = name + self.module = module + self.imported_from = None + self.fields = [] + self.attributes = attributes + + def AddField(self, name, kind, ordinal=None, attributes=None): + field = UnionField(name, kind, ordinal, None, attributes) + self.fields.append(field) + return field + + +class Array(ReferenceKind): + ReferenceKind.AddSharedProperty('kind') + ReferenceKind.AddSharedProperty('length') + + def __init__(self, kind=None, length=None): + if kind is not None: + if length is not None: + spec = 'a%d:%s' % (length, kind.spec) + else: + spec = 'a:%s' % kind.spec + + ReferenceKind.__init__(self, spec) + else: + ReferenceKind.__init__(self) + self.kind = kind + self.length = length + + +class Map(ReferenceKind): + ReferenceKind.AddSharedProperty('key_kind') + ReferenceKind.AddSharedProperty('value_kind') + + def __init__(self, key_kind=None, value_kind=None): + if (key_kind is not None and value_kind is not None): + ReferenceKind.__init__(self, + 'm[' + key_kind.spec + '][' + value_kind.spec + + ']') + if IsNullableKind(key_kind): + raise Exception("Nullable kinds cannot be keys in maps.") + if IsStructKind(key_kind): + # TODO(erg): It would sometimes be nice if we could key on struct + # values. However, what happens if the struct has a handle in it? Or + # non-copyable data like an array? + raise Exception("Structs cannot be keys in maps.") + if IsAnyHandleKind(key_kind): + raise Exception("Handles cannot be keys in maps.") + if IsInterfaceKind(key_kind): + raise Exception("Interfaces cannot be keys in maps.") + if IsArrayKind(key_kind): + raise Exception("Arrays cannot be keys in maps.") + else: + ReferenceKind.__init__(self) + + self.key_kind = key_kind + self.value_kind = value_kind + + +class InterfaceRequest(ReferenceKind): + ReferenceKind.AddSharedProperty('kind') + + def __init__(self, kind=None): + if kind is not None: + if not isinstance(kind, Interface): + raise Exception( + "Interface request requires %r to be an interface." % kind.spec) + ReferenceKind.__init__(self, 'r:' + kind.spec) + else: + ReferenceKind.__init__(self) + self.kind = kind + + +class AssociatedInterfaceRequest(ReferenceKind): + ReferenceKind.AddSharedProperty('kind') + + def __init__(self, kind=None): + if kind is not None: + if not isinstance(kind, InterfaceRequest): + raise Exception( + "Associated interface request requires %r to be an interface " + "request." % kind.spec) + assert not kind.is_nullable + ReferenceKind.__init__(self, 'asso:' + kind.spec) + else: + ReferenceKind.__init__(self) + self.kind = kind.kind if kind is not None else None + + +class Parameter(object): + def __init__(self, name=None, kind=None, ordinal=None, default=None, + attributes=None): + self.name = name + self.ordinal = ordinal + self.kind = kind + self.default = default + self.attributes = attributes + + @property + def min_version(self): + return self.attributes.get(ATTRIBUTE_MIN_VERSION) \ + if self.attributes else None + + +class Method(object): + def __init__(self, interface, name, ordinal=None, attributes=None): + self.interface = interface + self.name = name + self.ordinal = ordinal + self.parameters = [] + self.response_parameters = None + self.attributes = attributes + + def AddParameter(self, name, kind, ordinal=None, default=None, + attributes=None): + parameter = Parameter(name, kind, ordinal, default, attributes) + self.parameters.append(parameter) + return parameter + + def AddResponseParameter(self, name, kind, ordinal=None, default=None, + attributes=None): + if self.response_parameters == None: + self.response_parameters = [] + parameter = Parameter(name, kind, ordinal, default, attributes) + self.response_parameters.append(parameter) + return parameter + + @property + def min_version(self): + return self.attributes.get(ATTRIBUTE_MIN_VERSION) \ + if self.attributes else None + + +class Interface(ReferenceKind): + ReferenceKind.AddSharedProperty('module') + ReferenceKind.AddSharedProperty('name') + ReferenceKind.AddSharedProperty('imported_from') + ReferenceKind.AddSharedProperty('methods') + ReferenceKind.AddSharedProperty('attributes') + + def __init__(self, name=None, module=None, attributes=None): + if name is not None: + spec = 'x:' + name + else: + spec = None + ReferenceKind.__init__(self, spec) + self.module = module + self.name = name + self.imported_from = None + self.methods = [] + self.attributes = attributes + + def AddMethod(self, name, ordinal=None, attributes=None): + method = Method(self, name, ordinal, attributes) + self.methods.append(method) + return method + + # TODO(451323): Remove when the language backends no longer rely on this. + @property + def client(self): + return None + + +class AssociatedInterface(ReferenceKind): + ReferenceKind.AddSharedProperty('kind') + + def __init__(self, kind=None): + if kind is not None: + if not isinstance(kind, Interface): + raise Exception( + "Associated interface requires %r to be an interface." % kind.spec) + assert not kind.is_nullable + ReferenceKind.__init__(self, 'asso:' + kind.spec) + else: + ReferenceKind.__init__(self) + self.kind = kind + + +class EnumField(object): + def __init__(self, name=None, value=None, attributes=None, + numeric_value=None): + self.name = name + self.value = value + self.attributes = attributes + self.numeric_value = numeric_value + + @property + def min_version(self): + return self.attributes.get(ATTRIBUTE_MIN_VERSION) \ + if self.attributes else None + + +class Enum(Kind): + def __init__(self, name=None, module=None, attributes=None): + self.module = module + self.name = name + self.imported_from = None + if name is not None: + spec = 'x:' + name + else: + spec = None + Kind.__init__(self, spec) + self.fields = [] + self.attributes = attributes + + +class Module(object): + def __init__(self, name=None, namespace=None, attributes=None): + self.name = name + self.path = name + self.namespace = namespace + self.structs = [] + self.unions = [] + self.interfaces = [] + self.kinds = {} + self.attributes = attributes + + def AddInterface(self, name, attributes=None): + interface = Interface(name, self, attributes) + self.interfaces.append(interface) + return interface + + def AddStruct(self, name, attributes=None): + struct = Struct(name, self, attributes) + self.structs.append(struct) + return struct + + def AddUnion(self, name, attributes=None): + union = Union(name, self, attributes) + self.unions.append(union) + return union + + +def IsBoolKind(kind): + return kind.spec == BOOL.spec + + +def IsFloatKind(kind): + return kind.spec == FLOAT.spec + + +def IsIntegralKind(kind): + return (kind.spec == BOOL.spec or + kind.spec == INT8.spec or + kind.spec == INT16.spec or + kind.spec == INT32.spec or + kind.spec == INT64.spec or + kind.spec == UINT8.spec or + kind.spec == UINT16.spec or + kind.spec == UINT32.spec or + kind.spec == UINT64.spec) + + +def IsStringKind(kind): + return kind.spec == STRING.spec or kind.spec == NULLABLE_STRING.spec + + +def IsGenericHandleKind(kind): + return kind.spec == HANDLE.spec or kind.spec == NULLABLE_HANDLE.spec + + +def IsDataPipeConsumerKind(kind): + return kind.spec == DCPIPE.spec or kind.spec == NULLABLE_DCPIPE.spec + + +def IsDataPipeProducerKind(kind): + return kind.spec == DPPIPE.spec or kind.spec == NULLABLE_DPPIPE.spec + + +def IsMessagePipeKind(kind): + return kind.spec == MSGPIPE.spec or kind.spec == NULLABLE_MSGPIPE.spec + + +def IsSharedBufferKind(kind): + return (kind.spec == SHAREDBUFFER.spec or + kind.spec == NULLABLE_SHAREDBUFFER.spec) + + +def IsStructKind(kind): + return isinstance(kind, Struct) + + +def IsUnionKind(kind): + return isinstance(kind, Union) + + +def IsArrayKind(kind): + return isinstance(kind, Array) + + +def IsInterfaceKind(kind): + return isinstance(kind, Interface) + + +def IsAssociatedInterfaceKind(kind): + return isinstance(kind, AssociatedInterface) + + +def IsInterfaceRequestKind(kind): + return isinstance(kind, InterfaceRequest) + + +def IsAssociatedInterfaceRequestKind(kind): + return isinstance(kind, AssociatedInterfaceRequest) + + +def IsEnumKind(kind): + return isinstance(kind, Enum) + + +def IsReferenceKind(kind): + return isinstance(kind, ReferenceKind) + + +def IsNullableKind(kind): + return IsReferenceKind(kind) and kind.is_nullable + + +def IsMapKind(kind): + return isinstance(kind, Map) + + +def IsObjectKind(kind): + return IsPointerKind(kind) or IsUnionKind(kind) + + +def IsPointerKind(kind): + return (IsStructKind(kind) or IsArrayKind(kind) or IsStringKind(kind) or + IsMapKind(kind)) + + +# Please note that interface is not considered as handle kind, since it is an +# aggregate type consisting of a handle and a version number. +def IsAnyHandleKind(kind): + return (IsGenericHandleKind(kind) or + IsDataPipeConsumerKind(kind) or + IsDataPipeProducerKind(kind) or + IsMessagePipeKind(kind) or + IsSharedBufferKind(kind) or + IsInterfaceRequestKind(kind)) + + +def IsAssociatedKind(kind): + return (IsAssociatedInterfaceKind(kind) or + IsAssociatedInterfaceRequestKind(kind)) + + +def IsMoveOnlyKind(kind): + return (not IsStringKind(kind) and IsObjectKind(kind)) or \ + IsAnyHandleKind(kind) or IsInterfaceKind(kind) or IsAssociatedKind(kind) + + +def IsCloneableKind(kind): + def _IsCloneable(kind, visited_kinds): + if kind in visited_kinds: + # No need to examine the kind again. + return True + visited_kinds.add(kind) + if IsAnyHandleKind(kind) or IsInterfaceKind(kind) or IsAssociatedKind(kind): + return False + if IsArrayKind(kind): + return _IsCloneable(kind.kind, visited_kinds) + if IsStructKind(kind) or IsUnionKind(kind): + for field in kind.fields: + if not _IsCloneable(field.kind, visited_kinds): + return False + if IsMapKind(kind): + # No need to examine the key kind, only primitive kinds and non-nullable + # string are allowed to be key kinds. + return _IsCloneable(kind.value_kind, visited_kinds) + return True + + return _IsCloneable(kind, set()) + + +def HasCallbacks(interface): + for method in interface.methods: + if method.response_parameters != None: + return True + return False diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/module_tests.py b/mojo/public/tools/bindings/pylib/mojom/generate/module_tests.py new file mode 100644 index 0000000..a887686 --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom/generate/module_tests.py @@ -0,0 +1,34 @@ +# 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 test_support + +EXPECT_EQ = test_support.EXPECT_EQ +EXPECT_TRUE = test_support.EXPECT_TRUE +RunTest = test_support.RunTest +ModulesAreEqual = test_support.ModulesAreEqual +BuildTestModule = test_support.BuildTestModule +TestTestModule = test_support.TestTestModule + + +def BuildAndTestModule(): + return TestTestModule(BuildTestModule()) + + +def TestModulesEqual(): + return EXPECT_TRUE(ModulesAreEqual(BuildTestModule(), BuildTestModule())) + + +def Main(args): + errors = 0 + errors += RunTest(BuildAndTestModule) + errors += RunTest(TestModulesEqual) + + return errors + + +if __name__ == '__main__': + sys.exit(Main(sys.argv[1:])) diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/pack.py b/mojo/public/tools/bindings/pylib/mojom/generate/pack.py new file mode 100644 index 0000000..37dc8f3 --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom/generate/pack.py @@ -0,0 +1,250 @@ +# 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 module as mojom + +# This module provides a mechanism for determining the packed order and offsets +# of a mojom.Struct. +# +# ps = pack.PackedStruct(struct) +# ps.packed_fields will access a list of PackedField objects, each of which +# will have an offset, a size and a bit (for mojom.BOOLs). + +# Size of struct header in bytes: num_bytes [4B] + version [4B]. +HEADER_SIZE = 8 + +class PackedField(object): + kind_to_size = { + mojom.BOOL: 1, + mojom.INT8: 1, + mojom.UINT8: 1, + mojom.INT16: 2, + mojom.UINT16: 2, + mojom.INT32: 4, + mojom.UINT32: 4, + mojom.FLOAT: 4, + mojom.HANDLE: 4, + mojom.MSGPIPE: 4, + mojom.SHAREDBUFFER: 4, + mojom.DCPIPE: 4, + mojom.DPPIPE: 4, + mojom.NULLABLE_HANDLE: 4, + mojom.NULLABLE_MSGPIPE: 4, + mojom.NULLABLE_SHAREDBUFFER: 4, + mojom.NULLABLE_DCPIPE: 4, + mojom.NULLABLE_DPPIPE: 4, + mojom.INT64: 8, + mojom.UINT64: 8, + mojom.DOUBLE: 8, + mojom.STRING: 8, + mojom.NULLABLE_STRING: 8 + } + + @classmethod + def GetSizeForKind(cls, kind): + if isinstance(kind, (mojom.Array, mojom.Map, mojom.Struct, + mojom.Interface, mojom.AssociatedInterface)): + return 8 + if isinstance(kind, mojom.Union): + return 16 + if isinstance(kind, mojom.InterfaceRequest): + kind = mojom.MSGPIPE + if isinstance(kind, mojom.AssociatedInterfaceRequest): + return 4 + if isinstance(kind, mojom.Enum): + # TODO(mpcomplete): what about big enums? + return cls.kind_to_size[mojom.INT32] + if not kind in cls.kind_to_size: + raise Exception("Invalid kind: %s" % kind.spec) + return cls.kind_to_size[kind] + + @classmethod + def GetAlignmentForKind(cls, kind): + if isinstance(kind, (mojom.Interface, mojom.AssociatedInterface)): + return 4 + if isinstance(kind, mojom.Union): + return 8 + return cls.GetSizeForKind(kind) + + def __init__(self, field, index, ordinal): + """ + Args: + field: the original field. + index: the position of the original field in the struct. + ordinal: the ordinal of the field for serialization. + """ + self.field = field + self.index = index + self.ordinal = ordinal + self.size = self.GetSizeForKind(field.kind) + self.alignment = self.GetAlignmentForKind(field.kind) + self.offset = None + self.bit = None + self.min_version = None + + +def GetPad(offset, alignment): + """Returns the pad necessary to reserve space so that |offset + pad| equals to + some multiple of |alignment|.""" + return (alignment - (offset % alignment)) % alignment + + +def GetFieldOffset(field, last_field): + """Returns a 2-tuple of the field offset and bit (for BOOLs).""" + if (field.field.kind == mojom.BOOL and + last_field.field.kind == mojom.BOOL and + last_field.bit < 7): + return (last_field.offset, last_field.bit + 1) + + offset = last_field.offset + last_field.size + pad = GetPad(offset, field.alignment) + return (offset + pad, 0) + + +def GetPayloadSizeUpToField(field): + """Returns the payload size (not including struct header) if |field| is the + last field. + """ + if not field: + return 0 + offset = field.offset + field.size + pad = GetPad(offset, 8) + return offset + pad + + +class PackedStruct(object): + def __init__(self, struct): + self.struct = struct + # |packed_fields| contains all the fields, in increasing offset order. + self.packed_fields = [] + # |packed_fields_in_ordinal_order| refers to the same fields as + # |packed_fields|, but in ordinal order. + self.packed_fields_in_ordinal_order = [] + + # No fields. + if (len(struct.fields) == 0): + return + + # Start by sorting by ordinal. + src_fields = self.packed_fields_in_ordinal_order + ordinal = 0 + for index, field in enumerate(struct.fields): + if field.ordinal is not None: + ordinal = field.ordinal + src_fields.append(PackedField(field, index, ordinal)) + ordinal += 1 + src_fields.sort(key=lambda field: field.ordinal) + + # Set |min_version| for each field. + next_min_version = 0 + for packed_field in src_fields: + if packed_field.field.min_version is None: + assert next_min_version == 0 + else: + assert packed_field.field.min_version >= next_min_version + next_min_version = packed_field.field.min_version + packed_field.min_version = next_min_version + + if (packed_field.min_version != 0 and + mojom.IsReferenceKind(packed_field.field.kind) and + not packed_field.field.kind.is_nullable): + raise Exception("Non-nullable fields are only allowed in version 0 of " + "a struct. %s.%s is defined with [MinVersion=%d]." + % (self.struct.name, packed_field.field.name, + packed_field.min_version)) + + src_field = src_fields[0] + src_field.offset = 0 + src_field.bit = 0 + dst_fields = self.packed_fields + dst_fields.append(src_field) + + # Then find first slot that each field will fit. + for src_field in src_fields[1:]: + last_field = dst_fields[0] + for i in xrange(1, len(dst_fields)): + next_field = dst_fields[i] + offset, bit = GetFieldOffset(src_field, last_field) + if offset + src_field.size <= next_field.offset: + # Found hole. + src_field.offset = offset + src_field.bit = bit + dst_fields.insert(i, src_field) + break + last_field = next_field + if src_field.offset is None: + # Add to end + src_field.offset, src_field.bit = GetFieldOffset(src_field, last_field) + dst_fields.append(src_field) + + +class ByteInfo(object): + def __init__(self): + self.is_padding = False + self.packed_fields = [] + + +def GetByteLayout(packed_struct): + total_payload_size = GetPayloadSizeUpToField( + packed_struct.packed_fields[-1] if packed_struct.packed_fields else None) + bytes = [ByteInfo() for i in xrange(total_payload_size)] + + limit_of_previous_field = 0 + for packed_field in packed_struct.packed_fields: + for i in xrange(limit_of_previous_field, packed_field.offset): + bytes[i].is_padding = True + bytes[packed_field.offset].packed_fields.append(packed_field) + limit_of_previous_field = packed_field.offset + packed_field.size + + for i in xrange(limit_of_previous_field, len(bytes)): + bytes[i].is_padding = True + + for byte in bytes: + # A given byte cannot both be padding and have a fields packed into it. + assert not (byte.is_padding and byte.packed_fields) + + return bytes + + +class VersionInfo(object): + def __init__(self, version, num_fields, num_bytes): + self.version = version + self.num_fields = num_fields + self.num_bytes = num_bytes + + +def GetVersionInfo(packed_struct): + """Get version information for a struct. + + Args: + packed_struct: A PackedStruct instance. + + Returns: + A non-empty list of VersionInfo instances, sorted by version in increasing + order. + Note: The version numbers may not be consecutive. + """ + versions = [] + last_version = 0 + last_num_fields = 0 + last_payload_size = 0 + + for packed_field in packed_struct.packed_fields_in_ordinal_order: + if packed_field.min_version != last_version: + versions.append( + VersionInfo(last_version, last_num_fields, + last_payload_size + HEADER_SIZE)) + last_version = packed_field.min_version + + last_num_fields += 1 + # The fields are iterated in ordinal order here. However, the size of a + # version is determined by the last field of that version in pack order, + # instead of ordinal order. Therefore, we need to calculate the max value. + last_payload_size = max(GetPayloadSizeUpToField(packed_field), + last_payload_size) + + assert len(versions) == 0 or last_num_fields != versions[-1].num_fields + versions.append(VersionInfo(last_version, last_num_fields, + last_payload_size + HEADER_SIZE)) + return versions diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/pack_tests.py b/mojo/public/tools/bindings/pylib/mojom/generate/pack_tests.py new file mode 100644 index 0000000..14f699d --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom/generate/pack_tests.py @@ -0,0 +1,193 @@ +# 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 module as mojom +import pack +import test_support + + +EXPECT_EQ = test_support.EXPECT_EQ +EXPECT_TRUE = test_support.EXPECT_TRUE +RunTest = test_support.RunTest + + +def TestOrdinalOrder(): + errors = 0 + struct = mojom.Struct('test') + struct.AddField('testfield1', mojom.INT32, 2) + struct.AddField('testfield2', mojom.INT32, 1) + ps = pack.PackedStruct(struct) + + errors += EXPECT_EQ(2, len(ps.packed_fields)) + errors += EXPECT_EQ('testfield2', ps.packed_fields[0].field.name) + errors += EXPECT_EQ('testfield1', ps.packed_fields[1].field.name) + + return errors + +def TestZeroFields(): + errors = 0 + struct = mojom.Struct('test') + ps = pack.PackedStruct(struct) + errors += EXPECT_EQ(0, len(ps.packed_fields)) + return errors + + +def TestOneField(): + errors = 0 + struct = mojom.Struct('test') + struct.AddField('testfield1', mojom.INT8) + ps = pack.PackedStruct(struct) + errors += EXPECT_EQ(1, len(ps.packed_fields)) + return errors + +# Pass three tuples. +# |kinds| is a sequence of mojom.Kinds that specify the fields that are to +# be created. +# |fields| is the expected order of the resulting fields, with the integer +# "1" first. +# |offsets| is the expected order of offsets, with the integer "0" first. +def TestSequence(kinds, fields, offsets): + errors = 0 + struct = mojom.Struct('test') + index = 1 + for kind in kinds: + struct.AddField("%d" % index, kind) + index += 1 + ps = pack.PackedStruct(struct) + num_fields = len(ps.packed_fields) + errors += EXPECT_EQ(len(kinds), num_fields) + for i in xrange(num_fields): + EXPECT_EQ("%d" % fields[i], ps.packed_fields[i].field.name) + EXPECT_EQ(offsets[i], ps.packed_fields[i].offset) + + return errors + + +def TestPaddingPackedInOrder(): + return TestSequence( + (mojom.INT8, mojom.UINT8, mojom.INT32), + (1, 2, 3), + (0, 1, 4)) + + +def TestPaddingPackedOutOfOrder(): + return TestSequence( + (mojom.INT8, mojom.INT32, mojom.UINT8), + (1, 3, 2), + (0, 1, 4)) + + +def TestPaddingPackedOverflow(): + kinds = (mojom.INT8, mojom.INT32, mojom.INT16, mojom.INT8, mojom.INT8) + # 2 bytes should be packed together first, followed by short, then by int. + fields = (1, 4, 3, 2, 5) + offsets = (0, 1, 2, 4, 8) + return TestSequence(kinds, fields, offsets) + + +def TestNullableTypes(): + kinds = (mojom.STRING.MakeNullableKind(), + mojom.HANDLE.MakeNullableKind(), + mojom.Struct('test_struct').MakeNullableKind(), + mojom.DCPIPE.MakeNullableKind(), + mojom.Array().MakeNullableKind(), + mojom.DPPIPE.MakeNullableKind(), + mojom.Array(length=5).MakeNullableKind(), + mojom.MSGPIPE.MakeNullableKind(), + mojom.Interface('test_inteface').MakeNullableKind(), + mojom.SHAREDBUFFER.MakeNullableKind(), + mojom.InterfaceRequest().MakeNullableKind()) + fields = (1, 2, 4, 3, 5, 6, 8, 7, 9, 10, 11) + offsets = (0, 8, 12, 16, 24, 32, 36, 40, 48, 52, 56) + return TestSequence(kinds, fields, offsets) + + +def TestAllTypes(): + return TestSequence( + (mojom.BOOL, mojom.INT8, mojom.STRING, mojom.UINT8, + mojom.INT16, mojom.DOUBLE, mojom.UINT16, + mojom.INT32, mojom.UINT32, mojom.INT64, + mojom.FLOAT, mojom.STRING, mojom.HANDLE, + mojom.UINT64, mojom.Struct('test'), mojom.Array(), + mojom.STRING.MakeNullableKind()), + (1, 2, 4, 5, 7, 3, 6, 8, 9, 10, 11, 13, 12, 14, 15, 16, 17, 18), + (0, 1, 2, 4, 6, 8, 16, 24, 28, 32, 40, 44, 48, 56, 64, 72, 80, 88)) + + +def TestPaddingPackedOutOfOrderByOrdinal(): + errors = 0 + struct = mojom.Struct('test') + struct.AddField('testfield1', mojom.INT8) + struct.AddField('testfield3', mojom.UINT8, 3) + struct.AddField('testfield2', mojom.INT32, 2) + ps = pack.PackedStruct(struct) + errors += EXPECT_EQ(3, len(ps.packed_fields)) + + # Second byte should be packed in behind first, altering order. + errors += EXPECT_EQ('testfield1', ps.packed_fields[0].field.name) + errors += EXPECT_EQ('testfield3', ps.packed_fields[1].field.name) + errors += EXPECT_EQ('testfield2', ps.packed_fields[2].field.name) + + # Second byte should be packed with first. + errors += EXPECT_EQ(0, ps.packed_fields[0].offset) + errors += EXPECT_EQ(1, ps.packed_fields[1].offset) + errors += EXPECT_EQ(4, ps.packed_fields[2].offset) + + return errors + + +def TestBools(): + errors = 0 + struct = mojom.Struct('test') + struct.AddField('bit0', mojom.BOOL) + struct.AddField('bit1', mojom.BOOL) + struct.AddField('int', mojom.INT32) + struct.AddField('bit2', mojom.BOOL) + struct.AddField('bit3', mojom.BOOL) + struct.AddField('bit4', mojom.BOOL) + struct.AddField('bit5', mojom.BOOL) + struct.AddField('bit6', mojom.BOOL) + struct.AddField('bit7', mojom.BOOL) + struct.AddField('bit8', mojom.BOOL) + ps = pack.PackedStruct(struct) + errors += EXPECT_EQ(10, len(ps.packed_fields)) + + # First 8 bits packed together. + for i in xrange(8): + pf = ps.packed_fields[i] + errors += EXPECT_EQ(0, pf.offset) + errors += EXPECT_EQ("bit%d" % i, pf.field.name) + errors += EXPECT_EQ(i, pf.bit) + + # Ninth bit goes into second byte. + errors += EXPECT_EQ("bit8", ps.packed_fields[8].field.name) + errors += EXPECT_EQ(1, ps.packed_fields[8].offset) + errors += EXPECT_EQ(0, ps.packed_fields[8].bit) + + # int comes last. + errors += EXPECT_EQ("int", ps.packed_fields[9].field.name) + errors += EXPECT_EQ(4, ps.packed_fields[9].offset) + + return errors + + +def Main(args): + errors = 0 + errors += RunTest(TestZeroFields) + errors += RunTest(TestOneField) + errors += RunTest(TestPaddingPackedInOrder) + errors += RunTest(TestPaddingPackedOutOfOrder) + errors += RunTest(TestPaddingPackedOverflow) + errors += RunTest(TestNullableTypes) + errors += RunTest(TestAllTypes) + errors += RunTest(TestPaddingPackedOutOfOrderByOrdinal) + errors += RunTest(TestBools) + + return errors + + +if __name__ == '__main__': + sys.exit(Main(sys.argv[1:])) diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/run_tests.py b/mojo/public/tools/bindings/pylib/mojom/generate/run_tests.py new file mode 100755 index 0000000..41f11a2 --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom/generate/run_tests.py @@ -0,0 +1,35 @@ +#!/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. + +""" Test runner for Mojom """ + +import subprocess +import sys + +def TestMojom(testname, args): + print '\nRunning unit tests for %s.' % testname + try: + args = [sys.executable, testname] + args + subprocess.check_call(args, stdout=sys.stdout) + print 'Succeeded' + return 0 + except subprocess.CalledProcessError as err: + print 'Failed with %s.' % str(err) + return 1 + + +def main(args): + errors = 0 + errors += TestMojom('data_tests.py', ['--test']) + errors += TestMojom('module_tests.py', ['--test']) + errors += TestMojom('pack_tests.py', ['--test']) + + if errors: + print '\nFailed tests.' + return min(errors, 127) # Make sure the return value doesn't "wrap". + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py b/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py new file mode 100644 index 0000000..94db92e --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py @@ -0,0 +1,59 @@ +# 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. + +# Based on: +# http://src.chromium.org/viewvc/blink/trunk/Source/build/scripts/template_expander.py + +import imp +import inspect +import os.path +import sys + +# Disable lint check for finding modules: +# pylint: disable=F0401 + +def _GetDirAbove(dirname): + """Returns the directory "above" this file containing |dirname| (which must + also be "above" this file).""" + path = os.path.abspath(__file__) + while True: + path, tail = os.path.split(path) + assert tail + if tail == dirname: + return path + +try: + imp.find_module("jinja2") +except ImportError: + sys.path.append(os.path.join(_GetDirAbove("public"), "public/third_party")) +import jinja2 + + +def ApplyTemplate(mojo_generator, base_dir, path_to_template, params, + filters=None, **kwargs): + template_directory, template_name = os.path.split(path_to_template) + path_to_templates = os.path.join(base_dir, template_directory) + loader = jinja2.FileSystemLoader([path_to_templates]) + final_kwargs = dict(mojo_generator.GetJinjaParameters()) + final_kwargs.update(kwargs) + jinja_env = jinja2.Environment(loader=loader, keep_trailing_newline=True, + **final_kwargs) + jinja_env.globals.update(mojo_generator.GetGlobals()) + if filters: + jinja_env.filters.update(filters) + template = jinja_env.get_template(template_name) + return template.render(params) + + +def UseJinja(path_to_template, **kwargs): + # Get the directory of our caller's file. + base_dir = os.path.dirname(inspect.getfile(sys._getframe(1))) + def RealDecorator(generator): + def GeneratorInternal(*args, **kwargs2): + parameters = generator(*args, **kwargs2) + return ApplyTemplate(args[0], base_dir, path_to_template, parameters, + **kwargs) + GeneratorInternal.func_name = generator.func_name + return GeneratorInternal + return RealDecorator diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/test_support.py b/mojo/public/tools/bindings/pylib/mojom/generate/test_support.py new file mode 100644 index 0000000..eb39461 --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom/generate/test_support.py @@ -0,0 +1,193 @@ +# 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 traceback + +import module as mojom + +# Support for writing mojom test cases. +# RunTest(fn) will execute fn, catching any exceptions. fn should return +# the number of errors that are encountered. +# +# EXPECT_EQ(a, b) and EXPECT_TRUE(b) will print error information if the +# expectations are not true and return a non zero value. This allows test cases +# to be written like this +# +# def Foo(): +# errors = 0 +# errors += EXPECT_EQ('test', test()) +# ... +# return errors +# +# RunTest(foo) + +def FieldsAreEqual(field1, field2): + if field1 == field2: + return True + return field1.name == field2.name and \ + KindsAreEqual(field1.kind, field2.kind) and \ + field1.ordinal == field2.ordinal and \ + field1.default == field2.default + + +def KindsAreEqual(kind1, kind2): + if kind1 == kind2: + return True + if kind1.__class__ != kind2.__class__ or kind1.spec != kind2.spec: + return False + if kind1.__class__ == mojom.Kind: + return kind1.spec == kind2.spec + if kind1.__class__ == mojom.Struct: + if kind1.name != kind2.name or \ + kind1.spec != kind2.spec or \ + len(kind1.fields) != len(kind2.fields): + return False + for i in range(len(kind1.fields)): + if not FieldsAreEqual(kind1.fields[i], kind2.fields[i]): + return False + return True + if kind1.__class__ == mojom.Array: + return KindsAreEqual(kind1.kind, kind2.kind) + print 'Unknown Kind class: ', kind1.__class__.__name__ + return False + + +def ParametersAreEqual(parameter1, parameter2): + if parameter1 == parameter2: + return True + return parameter1.name == parameter2.name and \ + parameter1.ordinal == parameter2.ordinal and \ + parameter1.default == parameter2.default and \ + KindsAreEqual(parameter1.kind, parameter2.kind) + + +def MethodsAreEqual(method1, method2): + if method1 == method2: + return True + if method1.name != method2.name or \ + method1.ordinal != method2.ordinal or \ + len(method1.parameters) != len(method2.parameters): + return False + for i in range(len(method1.parameters)): + if not ParametersAreEqual(method1.parameters[i], method2.parameters[i]): + return False + return True + + +def InterfacesAreEqual(interface1, interface2): + if interface1 == interface2: + return True + if interface1.name != interface2.name or \ + len(interface1.methods) != len(interface2.methods): + return False + for i in range(len(interface1.methods)): + if not MethodsAreEqual(interface1.methods[i], interface2.methods[i]): + return False + return True + + +def ModulesAreEqual(module1, module2): + if module1 == module2: + return True + if module1.name != module2.name or \ + module1.namespace != module2.namespace or \ + len(module1.structs) != len(module2.structs) or \ + len(module1.interfaces) != len(module2.interfaces): + return False + for i in range(len(module1.structs)): + if not KindsAreEqual(module1.structs[i], module2.structs[i]): + return False + for i in range(len(module1.interfaces)): + if not InterfacesAreEqual(module1.interfaces[i], module2.interfaces[i]): + return False + return True + + +# Builds and returns a Module suitable for testing/ +def BuildTestModule(): + module = mojom.Module('test', 'testspace') + struct = module.AddStruct('teststruct') + struct.AddField('testfield1', mojom.INT32) + struct.AddField('testfield2', mojom.Array(mojom.INT32), 42) + + interface = module.AddInterface('Server') + method = interface.AddMethod('Foo', 42) + method.AddParameter('foo', mojom.INT32) + method.AddParameter('bar', mojom.Array(struct)) + + return module + + +# Tests if |module| is as built by BuildTestModule(). Returns the number of +# errors +def TestTestModule(module): + errors = 0 + + errors += EXPECT_EQ('test', module.name) + errors += EXPECT_EQ('testspace', module.namespace) + errors += EXPECT_EQ(1, len(module.structs)) + errors += EXPECT_EQ('teststruct', module.structs[0].name) + errors += EXPECT_EQ(2, len(module.structs[0].fields)) + errors += EXPECT_EQ('testfield1', module.structs[0].fields[0].name) + errors += EXPECT_EQ(mojom.INT32, module.structs[0].fields[0].kind) + errors += EXPECT_EQ('testfield2', module.structs[0].fields[1].name) + errors += EXPECT_EQ(mojom.Array, module.structs[0].fields[1].kind.__class__) + errors += EXPECT_EQ(mojom.INT32, module.structs[0].fields[1].kind.kind) + + errors += EXPECT_EQ(1, len(module.interfaces)) + errors += EXPECT_EQ('Server', module.interfaces[0].name) + errors += EXPECT_EQ(1, len(module.interfaces[0].methods)) + errors += EXPECT_EQ('Foo', module.interfaces[0].methods[0].name) + errors += EXPECT_EQ(2, len(module.interfaces[0].methods[0].parameters)) + errors += EXPECT_EQ('foo', module.interfaces[0].methods[0].parameters[0].name) + errors += EXPECT_EQ(mojom.INT32, + module.interfaces[0].methods[0].parameters[0].kind) + errors += EXPECT_EQ('bar', module.interfaces[0].methods[0].parameters[1].name) + errors += EXPECT_EQ( + mojom.Array, + module.interfaces[0].methods[0].parameters[1].kind.__class__) + errors += EXPECT_EQ( + module.structs[0], + module.interfaces[0].methods[0].parameters[1].kind.kind) + return errors + + +def PrintFailure(string): + stack = traceback.extract_stack() + frame = stack[len(stack)-3] + sys.stderr.write("ERROR at %s:%d, %s\n" % (frame[0], frame[1], string)) + print "Traceback:" + for line in traceback.format_list(stack[:len(stack)-2]): + sys.stderr.write(line) + + +def EXPECT_EQ(a, b): + if a != b: + PrintFailure("%s != %s" % (a, b)) + return 1 + return 0 + + +def EXPECT_TRUE(a): + if not a: + PrintFailure('Expecting True') + return 1 + return 0 + + +def RunTest(fn): + sys.stdout.write('Running %s...' % fn.__name__) + try: + errors = fn() + except: + traceback.print_exc(sys.stderr) + errors = 1 + if errors == 0: + sys.stdout.write('OK\n') + elif errors == 1: + sys.stdout.write('1 ERROR\n') + else: + sys.stdout.write('%d ERRORS\n' % errors) + return errors diff --git a/mojo/public/tools/bindings/pylib/mojom/parse/__init__.py b/mojo/public/tools/bindings/pylib/mojom/parse/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom/parse/__init__.py diff --git a/mojo/public/tools/bindings/pylib/mojom/parse/ast.py b/mojo/public/tools/bindings/pylib/mojom/parse/ast.py new file mode 100644 index 0000000..ffb44e0 --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom/parse/ast.py @@ -0,0 +1,410 @@ +# 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. + +"""Node classes for the AST for a Mojo IDL file.""" + +# Note: For convenience of testing, you probably want to define __eq__() methods +# for all node types; it's okay to be slightly lax (e.g., not compare filename +# and lineno). You may also define __repr__() to help with analyzing test +# failures, especially for more complex types. + + +class NodeBase(object): + """Base class for nodes in the AST.""" + + def __init__(self, filename=None, lineno=None): + self.filename = filename + self.lineno = lineno + + def __eq__(self, other): + return type(self) == type(other) + + # Make != the inverse of ==. (Subclasses shouldn't have to override this.) + def __ne__(self, other): + return not self == other + + +# TODO(vtl): Some of this is complicated enough that it should be tested. +class NodeListBase(NodeBase): + """Represents a list of other nodes, all having the same type. (This is meant + to be subclassed, with subclasses defining _list_item_type to be the class (or + classes, in a tuple) of the members of the list.)""" + + def __init__(self, item_or_items=None, **kwargs): + super(NodeListBase, self).__init__(**kwargs) + self.items = [] + if item_or_items is None: + pass + elif isinstance(item_or_items, list): + for item in item_or_items: + assert isinstance(item, self._list_item_type) + self.Append(item) + else: + assert isinstance(item_or_items, self._list_item_type) + self.Append(item_or_items) + + # Support iteration. For everything else, users should just access |items| + # directly. (We intentionally do NOT supply |__len__()| or |__nonzero__()|, so + # |bool(NodeListBase())| is true.) + def __iter__(self): + return self.items.__iter__() + + def __eq__(self, other): + return super(NodeListBase, self).__eq__(other) and \ + self.items == other.items + + # Implement this so that on failure, we get slightly more sensible output. + def __repr__(self): + return self.__class__.__name__ + "([" + \ + ", ".join([repr(elem) for elem in self.items]) + "])" + + def Insert(self, item): + """Inserts item at the front of the list.""" + + assert isinstance(item, self._list_item_type) + self.items.insert(0, item) + self._UpdateFilenameAndLineno() + + def Append(self, item): + """Appends item to the end of the list.""" + + assert isinstance(item, self._list_item_type) + self.items.append(item) + self._UpdateFilenameAndLineno() + + def _UpdateFilenameAndLineno(self): + if self.items: + self.filename = self.items[0].filename + self.lineno = self.items[0].lineno + + +class Definition(NodeBase): + """Represents a definition of anything that has a global name (e.g., enums, + enum values, consts, structs, struct fields, interfaces). (This does not + include parameter definitions.) This class is meant to be subclassed.""" + + def __init__(self, name, **kwargs): + assert isinstance(name, str) + NodeBase.__init__(self, **kwargs) + self.name = name + + +################################################################################ + + +class Attribute(NodeBase): + """Represents an attribute.""" + + def __init__(self, key, value, **kwargs): + assert isinstance(key, str) + super(Attribute, self).__init__(**kwargs) + self.key = key + self.value = value + + def __eq__(self, other): + return super(Attribute, self).__eq__(other) and \ + self.key == other.key and \ + self.value == other.value + + +class AttributeList(NodeListBase): + """Represents a list attributes.""" + + _list_item_type = Attribute + + +class Const(Definition): + """Represents a const definition.""" + + def __init__(self, name, typename, value, **kwargs): + # The typename is currently passed through as a string. + assert isinstance(typename, str) + # The value is either a literal (currently passed through as a string) or a + # "wrapped identifier". + assert isinstance(value, str) or isinstance(value, tuple) + super(Const, self).__init__(name, **kwargs) + self.typename = typename + self.value = value + + def __eq__(self, other): + return super(Const, self).__eq__(other) and \ + self.typename == other.typename and \ + self.value == other.value + + +class Enum(Definition): + """Represents an enum definition.""" + + def __init__(self, name, attribute_list, enum_value_list, **kwargs): + assert attribute_list is None or isinstance(attribute_list, AttributeList) + assert isinstance(enum_value_list, EnumValueList) + super(Enum, self).__init__(name, **kwargs) + self.attribute_list = attribute_list + self.enum_value_list = enum_value_list + + def __eq__(self, other): + return super(Enum, self).__eq__(other) and \ + self.attribute_list == other.attribute_list and \ + self.enum_value_list == other.enum_value_list + + +class EnumValue(Definition): + """Represents a definition of an enum value.""" + + def __init__(self, name, attribute_list, value, **kwargs): + # The optional value is either an int (which is current a string) or a + # "wrapped identifier". + assert attribute_list is None or isinstance(attribute_list, AttributeList) + assert value is None or isinstance(value, (str, tuple)) + super(EnumValue, self).__init__(name, **kwargs) + self.attribute_list = attribute_list + self.value = value + + def __eq__(self, other): + return super(EnumValue, self).__eq__(other) and \ + self.attribute_list == other.attribute_list and \ + self.value == other.value + + +class EnumValueList(NodeListBase): + """Represents a list of enum value definitions (i.e., the "body" of an enum + definition).""" + + _list_item_type = EnumValue + + +class Import(NodeBase): + """Represents an import statement.""" + + def __init__(self, import_filename, **kwargs): + assert isinstance(import_filename, str) + super(Import, self).__init__(**kwargs) + self.import_filename = import_filename + + def __eq__(self, other): + return super(Import, self).__eq__(other) and \ + self.import_filename == other.import_filename + + +class ImportList(NodeListBase): + """Represents a list (i.e., sequence) of import statements.""" + + _list_item_type = Import + + +class Interface(Definition): + """Represents an interface definition.""" + + def __init__(self, name, attribute_list, body, **kwargs): + assert attribute_list is None or isinstance(attribute_list, AttributeList) + assert isinstance(body, InterfaceBody) + super(Interface, self).__init__(name, **kwargs) + self.attribute_list = attribute_list + self.body = body + + def __eq__(self, other): + return super(Interface, self).__eq__(other) and \ + self.attribute_list == other.attribute_list and \ + self.body == other.body + + +class Method(Definition): + """Represents a method definition.""" + + def __init__(self, name, attribute_list, ordinal, parameter_list, + response_parameter_list, **kwargs): + assert attribute_list is None or isinstance(attribute_list, AttributeList) + assert ordinal is None or isinstance(ordinal, Ordinal) + assert isinstance(parameter_list, ParameterList) + assert response_parameter_list is None or \ + isinstance(response_parameter_list, ParameterList) + super(Method, self).__init__(name, **kwargs) + self.attribute_list = attribute_list + self.ordinal = ordinal + self.parameter_list = parameter_list + self.response_parameter_list = response_parameter_list + + def __eq__(self, other): + return super(Method, self).__eq__(other) and \ + self.attribute_list == other.attribute_list and \ + self.ordinal == other.ordinal and \ + self.parameter_list == other.parameter_list and \ + self.response_parameter_list == other.response_parameter_list + + +# This needs to be declared after |Method|. +class InterfaceBody(NodeListBase): + """Represents the body of (i.e., list of definitions inside) an interface.""" + + _list_item_type = (Const, Enum, Method) + + +class Module(NodeBase): + """Represents a module statement.""" + + def __init__(self, name, attribute_list, **kwargs): + # |name| is either none or a "wrapped identifier". + assert name is None or isinstance(name, tuple) + assert attribute_list is None or isinstance(attribute_list, AttributeList) + super(Module, self).__init__(**kwargs) + self.name = name + self.attribute_list = attribute_list + + def __eq__(self, other): + return super(Module, self).__eq__(other) and \ + self.name == other.name and \ + self.attribute_list == other.attribute_list + + +class Mojom(NodeBase): + """Represents an entire .mojom file. (This is the root node.)""" + + def __init__(self, module, import_list, definition_list, **kwargs): + assert module is None or isinstance(module, Module) + assert isinstance(import_list, ImportList) + assert isinstance(definition_list, list) + super(Mojom, self).__init__(**kwargs) + self.module = module + self.import_list = import_list + self.definition_list = definition_list + + def __eq__(self, other): + return super(Mojom, self).__eq__(other) and \ + self.module == other.module and \ + self.import_list == other.import_list and \ + self.definition_list == other.definition_list + + def __repr__(self): + return "%s(%r, %r, %r)" % (self.__class__.__name__, self.module, + self.import_list, self.definition_list) + + +class Ordinal(NodeBase): + """Represents an ordinal value labeling, e.g., a struct field.""" + + def __init__(self, value, **kwargs): + assert isinstance(value, int) + super(Ordinal, self).__init__(**kwargs) + self.value = value + + def __eq__(self, other): + return super(Ordinal, self).__eq__(other) and \ + self.value == other.value + + +class Parameter(NodeBase): + """Represents a method request or response parameter.""" + + def __init__(self, name, attribute_list, ordinal, typename, **kwargs): + assert isinstance(name, str) + assert attribute_list is None or isinstance(attribute_list, AttributeList) + assert ordinal is None or isinstance(ordinal, Ordinal) + assert isinstance(typename, str) + super(Parameter, self).__init__(**kwargs) + self.name = name + self.attribute_list = attribute_list + self.ordinal = ordinal + self.typename = typename + + def __eq__(self, other): + return super(Parameter, self).__eq__(other) and \ + self.name == other.name and \ + self.attribute_list == other.attribute_list and \ + self.ordinal == other.ordinal and \ + self.typename == other.typename + + +class ParameterList(NodeListBase): + """Represents a list of (method request or response) parameters.""" + + _list_item_type = Parameter + + +class Struct(Definition): + """Represents a struct definition.""" + + def __init__(self, name, attribute_list, body, **kwargs): + assert attribute_list is None or isinstance(attribute_list, AttributeList) + assert isinstance(body, StructBody) + super(Struct, self).__init__(name, **kwargs) + self.attribute_list = attribute_list + self.body = body + + def __eq__(self, other): + return super(Struct, self).__eq__(other) and \ + self.attribute_list == other.attribute_list and \ + self.body == other.body + + +class StructField(Definition): + """Represents a struct field definition.""" + + def __init__(self, name, attribute_list, ordinal, typename, default_value, + **kwargs): + assert isinstance(name, str) + assert attribute_list is None or isinstance(attribute_list, AttributeList) + assert ordinal is None or isinstance(ordinal, Ordinal) + assert isinstance(typename, str) + # The optional default value is currently either a value as a string or a + # "wrapped identifier". + assert default_value is None or isinstance(default_value, (str, tuple)) + super(StructField, self).__init__(name, **kwargs) + self.attribute_list = attribute_list + self.ordinal = ordinal + self.typename = typename + self.default_value = default_value + + def __eq__(self, other): + return super(StructField, self).__eq__(other) and \ + self.attribute_list == other.attribute_list and \ + self.ordinal == other.ordinal and \ + self.typename == other.typename and \ + self.default_value == other.default_value + + +# This needs to be declared after |StructField|. +class StructBody(NodeListBase): + """Represents the body of (i.e., list of definitions inside) a struct.""" + + _list_item_type = (Const, Enum, StructField) + + +class Union(Definition): + """Represents a union definition.""" + + def __init__(self, name, attribute_list, body, **kwargs): + assert attribute_list is None or isinstance(attribute_list, AttributeList) + assert isinstance(body, UnionBody) + super(Union, self).__init__(name, **kwargs) + self.attribute_list = attribute_list + self.body = body + + def __eq__(self, other): + return super(Union, self).__eq__(other) and \ + self.attribute_list == other.attribute_list and \ + self.body == other.body + + +class UnionField(Definition): + + def __init__(self, name, attribute_list, ordinal, typename, **kwargs): + assert isinstance(name, str) + assert attribute_list is None or isinstance(attribute_list, AttributeList) + assert ordinal is None or isinstance(ordinal, Ordinal) + assert isinstance(typename, str) + super(UnionField, self).__init__(name, **kwargs) + self.attribute_list = attribute_list + self.ordinal = ordinal + self.typename = typename + + def __eq__(self, other): + return super(UnionField, self).__eq__(other) and \ + self.attribute_list == other.attribute_list and \ + self.ordinal == other.ordinal and \ + self.typename == other.typename + + +class UnionBody(NodeListBase): + + _list_item_type = UnionField diff --git a/mojo/public/tools/bindings/pylib/mojom/parse/lexer.py b/mojo/public/tools/bindings/pylib/mojom/parse/lexer.py new file mode 100644 index 0000000..fde2096 --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom/parse/lexer.py @@ -0,0 +1,254 @@ +# 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. + +import imp +import os.path +import sys + +def _GetDirAbove(dirname): + """Returns the directory "above" this file containing |dirname| (which must + also be "above" this file).""" + path = os.path.abspath(__file__) + while True: + path, tail = os.path.split(path) + assert tail + if tail == dirname: + return path + +try: + imp.find_module("ply") +except ImportError: + sys.path.append(os.path.join(_GetDirAbove("public"), "public/third_party")) +from ply.lex import TOKEN + +from ..error import Error + + +class LexError(Error): + """Class for errors from the lexer.""" + + def __init__(self, filename, message, lineno): + Error.__init__(self, filename, message, lineno=lineno) + + +# We have methods which look like they could be functions: +# pylint: disable=R0201 +class Lexer(object): + + def __init__(self, filename): + self.filename = filename + + ######################-- PRIVATE --###################### + + ## + ## Internal auxiliary methods + ## + def _error(self, msg, token): + raise LexError(self.filename, msg, token.lineno) + + ## + ## Reserved keywords + ## + keywords = ( + 'HANDLE', + + 'IMPORT', + 'MODULE', + 'STRUCT', + 'UNION', + 'INTERFACE', + 'ENUM', + 'CONST', + 'TRUE', + 'FALSE', + 'DEFAULT', + 'ARRAY', + 'MAP', + 'ASSOCIATED' + ) + + keyword_map = {} + for keyword in keywords: + keyword_map[keyword.lower()] = keyword + + ## + ## All the tokens recognized by the lexer + ## + tokens = keywords + ( + # Identifiers + 'NAME', + + # Constants + 'ORDINAL', + 'INT_CONST_DEC', 'INT_CONST_HEX', + 'FLOAT_CONST', + + # String literals + 'STRING_LITERAL', + + # Operators + 'MINUS', + 'PLUS', + 'AMP', + 'QSTN', + + # Assignment + 'EQUALS', + + # Request / response + 'RESPONSE', + + # Delimiters + 'LPAREN', 'RPAREN', # ( ) + 'LBRACKET', 'RBRACKET', # [ ] + 'LBRACE', 'RBRACE', # { } + 'LANGLE', 'RANGLE', # < > + 'SEMI', # ; + 'COMMA', 'DOT' # , . + ) + + ## + ## Regexes for use in tokens + ## + + # valid C identifiers (K&R2: A.2.3) + identifier = r'[a-zA-Z_][0-9a-zA-Z_]*' + + hex_prefix = '0[xX]' + hex_digits = '[0-9a-fA-F]+' + + # integer constants (K&R2: A.2.5.1) + decimal_constant = '0|([1-9][0-9]*)' + hex_constant = hex_prefix+hex_digits + # Don't allow octal constants (even invalid octal). + octal_constant_disallowed = '0[0-9]+' + + # character constants (K&R2: A.2.5.2) + # Note: a-zA-Z and '.-~^_!=&;,' are allowed as escape chars to support #line + # directives with Windows paths as filenames (..\..\dir\file) + # For the same reason, decimal_escape allows all digit sequences. We want to + # parse all correct code, even if it means to sometimes parse incorrect + # code. + # + simple_escape = r"""([a-zA-Z._~!=&\^\-\\?'"])""" + decimal_escape = r"""(\d+)""" + hex_escape = r"""(x[0-9a-fA-F]+)""" + bad_escape = r"""([\\][^a-zA-Z._~^!=&\^\-\\?'"x0-7])""" + + escape_sequence = \ + r"""(\\("""+simple_escape+'|'+decimal_escape+'|'+hex_escape+'))' + + # string literals (K&R2: A.2.6) + string_char = r"""([^"\\\n]|"""+escape_sequence+')' + string_literal = '"'+string_char+'*"' + bad_string_literal = '"'+string_char+'*'+bad_escape+string_char+'*"' + + # floating constants (K&R2: A.2.5.3) + exponent_part = r"""([eE][-+]?[0-9]+)""" + fractional_constant = r"""([0-9]*\.[0-9]+)|([0-9]+\.)""" + floating_constant = \ + '(((('+fractional_constant+')'+ \ + exponent_part+'?)|([0-9]+'+exponent_part+')))' + + # Ordinals + ordinal = r'@[0-9]+' + missing_ordinal_value = r'@' + # Don't allow ordinal values in octal (even invalid octal, like 09) or + # hexadecimal. + octal_or_hex_ordinal_disallowed = r'@((0[0-9]+)|('+hex_prefix+hex_digits+'))' + + ## + ## Rules for the normal state + ## + t_ignore = ' \t\r' + + # Newlines + def t_NEWLINE(self, t): + r'\n+' + t.lexer.lineno += len(t.value) + + # Operators + t_MINUS = r'-' + t_PLUS = r'\+' + t_AMP = r'&' + t_QSTN = r'\?' + + # = + t_EQUALS = r'=' + + # => + t_RESPONSE = r'=>' + + # Delimiters + t_LPAREN = r'\(' + t_RPAREN = r'\)' + t_LBRACKET = r'\[' + t_RBRACKET = r'\]' + t_LBRACE = r'\{' + t_RBRACE = r'\}' + t_LANGLE = r'<' + t_RANGLE = r'>' + t_COMMA = r',' + t_DOT = r'\.' + t_SEMI = r';' + + t_STRING_LITERAL = string_literal + + # The following floating and integer constants are defined as + # functions to impose a strict order (otherwise, decimal + # is placed before the others because its regex is longer, + # and this is bad) + # + @TOKEN(floating_constant) + def t_FLOAT_CONST(self, t): + return t + + @TOKEN(hex_constant) + def t_INT_CONST_HEX(self, t): + return t + + @TOKEN(octal_constant_disallowed) + def t_OCTAL_CONSTANT_DISALLOWED(self, t): + msg = "Octal values not allowed" + self._error(msg, t) + + @TOKEN(decimal_constant) + def t_INT_CONST_DEC(self, t): + return t + + # unmatched string literals are caught by the preprocessor + + @TOKEN(bad_string_literal) + def t_BAD_STRING_LITERAL(self, t): + msg = "String contains invalid escape code" + self._error(msg, t) + + # Handle ordinal-related tokens in the right order: + @TOKEN(octal_or_hex_ordinal_disallowed) + def t_OCTAL_OR_HEX_ORDINAL_DISALLOWED(self, t): + msg = "Octal and hexadecimal ordinal values not allowed" + self._error(msg, t) + + @TOKEN(ordinal) + def t_ORDINAL(self, t): + return t + + @TOKEN(missing_ordinal_value) + def t_BAD_ORDINAL(self, t): + msg = "Missing ordinal value" + self._error(msg, t) + + @TOKEN(identifier) + def t_NAME(self, t): + t.type = self.keyword_map.get(t.value, "NAME") + return t + + # Ignore C and C++ style comments + def t_COMMENT(self, t): + r'(/\*(.|\n)*?\*/)|(//.*(\n[ \t]*//.*)*)' + t.lexer.lineno += t.value.count("\n") + + def t_error(self, t): + msg = "Illegal character %s" % repr(t.value[0]) + self._error(msg, t) diff --git a/mojo/public/tools/bindings/pylib/mojom/parse/parser.py b/mojo/public/tools/bindings/pylib/mojom/parse/parser.py new file mode 100644 index 0000000..bdc13cf --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom/parse/parser.py @@ -0,0 +1,424 @@ +# 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. + +"""Generates a syntax tree from a Mojo IDL file.""" + +import imp +import os.path +import sys + +def _GetDirAbove(dirname): + """Returns the directory "above" this file containing |dirname| (which must + also be "above" this file).""" + path = os.path.abspath(__file__) + while True: + path, tail = os.path.split(path) + assert tail + if tail == dirname: + return path + +try: + imp.find_module("ply") +except ImportError: + sys.path.append(os.path.join(_GetDirAbove("public"), "public/third_party")) +from ply import lex +from ply import yacc + +from ..error import Error +from . import ast +from .lexer import Lexer + + +_MAX_ORDINAL_VALUE = 0xffffffff +_MAX_ARRAY_SIZE = 0xffffffff + + +class ParseError(Error): + """Class for errors from the parser.""" + + def __init__(self, filename, message, lineno=None, snippet=None): + Error.__init__(self, filename, message, lineno=lineno, + addenda=([snippet] if snippet else None)) + + +# We have methods which look like they could be functions: +# pylint: disable=R0201 +class Parser(object): + + def __init__(self, lexer, source, filename): + self.tokens = lexer.tokens + self.source = source + self.filename = filename + + # Names of functions + # + # In general, we name functions after the left-hand-side of the rule(s) that + # they handle. E.g., |p_foo_bar| for a rule |foo_bar : ...|. + # + # There may be multiple functions handling rules for the same left-hand-side; + # then we name the functions |p_foo_bar_N| (for left-hand-side |foo_bar|), + # where N is a number (numbered starting from 1). Note that using multiple + # functions is actually more efficient than having single functions handle + # multiple rules (and, e.g., distinguishing them by examining |len(p)|). + # + # It's also possible to have a function handling multiple rules with different + # left-hand-sides. We do not do this. + # + # See http://www.dabeaz.com/ply/ply.html#ply_nn25 for more details. + + # TODO(vtl): Get rid of the braces in the module "statement". (Consider + # renaming "module" -> "package".) Then we'll be able to have a single rule + # for root (by making module "optional"). + def p_root_1(self, p): + """root : """ + p[0] = ast.Mojom(None, ast.ImportList(), []) + + def p_root_2(self, p): + """root : root module""" + if p[1].module is not None: + raise ParseError(self.filename, + "Multiple \"module\" statements not allowed:", + p[2].lineno, snippet=self._GetSnippet(p[2].lineno)) + if p[1].import_list.items or p[1].definition_list: + raise ParseError( + self.filename, + "\"module\" statements must precede imports and definitions:", + p[2].lineno, snippet=self._GetSnippet(p[2].lineno)) + p[0] = p[1] + p[0].module = p[2] + + def p_root_3(self, p): + """root : root import""" + if p[1].definition_list: + raise ParseError(self.filename, + "\"import\" statements must precede definitions:", + p[2].lineno, snippet=self._GetSnippet(p[2].lineno)) + p[0] = p[1] + p[0].import_list.Append(p[2]) + + def p_root_4(self, p): + """root : root definition""" + p[0] = p[1] + p[0].definition_list.append(p[2]) + + def p_import(self, p): + """import : IMPORT STRING_LITERAL SEMI""" + # 'eval' the literal to strip the quotes. + # TODO(vtl): This eval is dubious. We should unquote/unescape ourselves. + p[0] = ast.Import(eval(p[2]), filename=self.filename, lineno=p.lineno(2)) + + def p_module(self, p): + """module : attribute_section MODULE identifier_wrapped SEMI""" + p[0] = ast.Module(p[3], p[1], filename=self.filename, lineno=p.lineno(2)) + + def p_definition(self, p): + """definition : struct + | union + | interface + | enum + | const""" + p[0] = p[1] + + def p_attribute_section_1(self, p): + """attribute_section : """ + p[0] = None + + def p_attribute_section_2(self, p): + """attribute_section : LBRACKET attribute_list RBRACKET""" + p[0] = p[2] + + def p_attribute_list_1(self, p): + """attribute_list : """ + p[0] = ast.AttributeList() + + def p_attribute_list_2(self, p): + """attribute_list : nonempty_attribute_list""" + p[0] = p[1] + + def p_nonempty_attribute_list_1(self, p): + """nonempty_attribute_list : attribute""" + p[0] = ast.AttributeList(p[1]) + + def p_nonempty_attribute_list_2(self, p): + """nonempty_attribute_list : nonempty_attribute_list COMMA attribute""" + p[0] = p[1] + p[0].Append(p[3]) + + def p_attribute(self, p): + """attribute : NAME EQUALS evaled_literal + | NAME EQUALS NAME""" + p[0] = ast.Attribute(p[1], p[3], filename=self.filename, lineno=p.lineno(1)) + + def p_evaled_literal(self, p): + """evaled_literal : literal""" + # 'eval' the literal to strip the quotes. + p[0] = eval(p[1]) + + def p_struct(self, p): + """struct : attribute_section STRUCT NAME LBRACE struct_body RBRACE SEMI""" + p[0] = ast.Struct(p[3], p[1], p[5]) + + def p_struct_body_1(self, p): + """struct_body : """ + p[0] = ast.StructBody() + + def p_struct_body_2(self, p): + """struct_body : struct_body const + | struct_body enum + | struct_body struct_field""" + p[0] = p[1] + p[0].Append(p[2]) + + def p_struct_field(self, p): + """struct_field : attribute_section typename NAME ordinal default SEMI""" + p[0] = ast.StructField(p[3], p[1], p[4], p[2], p[5]) + + def p_union(self, p): + """union : attribute_section UNION NAME LBRACE union_body RBRACE SEMI""" + p[0] = ast.Union(p[3], p[1], p[5]) + + def p_union_body_1(self, p): + """union_body : """ + p[0] = ast.UnionBody() + + def p_union_body_2(self, p): + """union_body : union_body union_field""" + p[0] = p[1] + p[1].Append(p[2]) + + def p_union_field(self, p): + """union_field : attribute_section typename NAME ordinal SEMI""" + p[0] = ast.UnionField(p[3], p[1], p[4], p[2]) + + def p_default_1(self, p): + """default : """ + p[0] = None + + def p_default_2(self, p): + """default : EQUALS constant""" + p[0] = p[2] + + def p_interface(self, p): + """interface : attribute_section INTERFACE NAME LBRACE interface_body \ + RBRACE SEMI""" + p[0] = ast.Interface(p[3], p[1], p[5]) + + def p_interface_body_1(self, p): + """interface_body : """ + p[0] = ast.InterfaceBody() + + def p_interface_body_2(self, p): + """interface_body : interface_body const + | interface_body enum + | interface_body method""" + p[0] = p[1] + p[0].Append(p[2]) + + def p_response_1(self, p): + """response : """ + p[0] = None + + def p_response_2(self, p): + """response : RESPONSE LPAREN parameter_list RPAREN""" + p[0] = p[3] + + def p_method(self, p): + """method : attribute_section NAME ordinal LPAREN parameter_list RPAREN \ + response SEMI""" + p[0] = ast.Method(p[2], p[1], p[3], p[5], p[7]) + + def p_parameter_list_1(self, p): + """parameter_list : """ + p[0] = ast.ParameterList() + + def p_parameter_list_2(self, p): + """parameter_list : nonempty_parameter_list""" + p[0] = p[1] + + def p_nonempty_parameter_list_1(self, p): + """nonempty_parameter_list : parameter""" + p[0] = ast.ParameterList(p[1]) + + def p_nonempty_parameter_list_2(self, p): + """nonempty_parameter_list : nonempty_parameter_list COMMA parameter""" + p[0] = p[1] + p[0].Append(p[3]) + + def p_parameter(self, p): + """parameter : attribute_section typename NAME ordinal""" + p[0] = ast.Parameter(p[3], p[1], p[4], p[2], + filename=self.filename, lineno=p.lineno(3)) + + def p_typename(self, p): + """typename : nonnullable_typename QSTN + | nonnullable_typename""" + if len(p) == 2: + p[0] = p[1] + else: + p[0] = p[1] + "?" + + def p_nonnullable_typename(self, p): + """nonnullable_typename : basictypename + | array + | fixed_array + | associative_array + | interfacerequest""" + p[0] = p[1] + + def p_basictypename(self, p): + """basictypename : identifier + | ASSOCIATED identifier + | handletype""" + if len(p) == 2: + p[0] = p[1] + else: + p[0] = "asso<" + p[2] + ">" + + def p_handletype(self, p): + """handletype : HANDLE + | HANDLE LANGLE NAME RANGLE""" + if len(p) == 2: + p[0] = p[1] + else: + if p[3] not in ('data_pipe_consumer', + 'data_pipe_producer', + 'message_pipe', + 'shared_buffer'): + # Note: We don't enable tracking of line numbers for everything, so we + # can't use |p.lineno(3)|. + raise ParseError(self.filename, "Invalid handle type %r:" % p[3], + lineno=p.lineno(1), + snippet=self._GetSnippet(p.lineno(1))) + p[0] = "handle<" + p[3] + ">" + + def p_array(self, p): + """array : ARRAY LANGLE typename RANGLE""" + p[0] = p[3] + "[]" + + def p_fixed_array(self, p): + """fixed_array : ARRAY LANGLE typename COMMA INT_CONST_DEC RANGLE""" + value = int(p[5]) + if value == 0 or value > _MAX_ARRAY_SIZE: + raise ParseError(self.filename, "Fixed array size %d invalid:" % value, + lineno=p.lineno(5), + snippet=self._GetSnippet(p.lineno(5))) + p[0] = p[3] + "[" + p[5] + "]" + + def p_associative_array(self, p): + """associative_array : MAP LANGLE identifier COMMA typename RANGLE""" + p[0] = p[5] + "{" + p[3] + "}" + + def p_interfacerequest(self, p): + """interfacerequest : identifier AMP + | ASSOCIATED identifier AMP""" + if len(p) == 3: + p[0] = p[1] + "&" + else: + p[0] = "asso<" + p[2] + "&>" + + def p_ordinal_1(self, p): + """ordinal : """ + p[0] = None + + def p_ordinal_2(self, p): + """ordinal : ORDINAL""" + value = int(p[1][1:]) + if value > _MAX_ORDINAL_VALUE: + raise ParseError(self.filename, "Ordinal value %d too large:" % value, + lineno=p.lineno(1), + snippet=self._GetSnippet(p.lineno(1))) + p[0] = ast.Ordinal(value, filename=self.filename, lineno=p.lineno(1)) + + def p_enum(self, p): + """enum : attribute_section ENUM NAME LBRACE nonempty_enum_value_list \ + RBRACE SEMI + | attribute_section ENUM NAME LBRACE nonempty_enum_value_list \ + COMMA RBRACE SEMI""" + p[0] = ast.Enum(p[3], p[1], p[5], filename=self.filename, + lineno=p.lineno(2)) + + def p_nonempty_enum_value_list_1(self, p): + """nonempty_enum_value_list : enum_value""" + p[0] = ast.EnumValueList(p[1]) + + def p_nonempty_enum_value_list_2(self, p): + """nonempty_enum_value_list : nonempty_enum_value_list COMMA enum_value""" + p[0] = p[1] + p[0].Append(p[3]) + + def p_enum_value(self, p): + """enum_value : attribute_section NAME + | attribute_section NAME EQUALS int + | attribute_section NAME EQUALS identifier_wrapped""" + p[0] = ast.EnumValue(p[2], p[1], p[4] if len(p) == 5 else None, + filename=self.filename, lineno=p.lineno(2)) + + def p_const(self, p): + """const : CONST typename NAME EQUALS constant SEMI""" + p[0] = ast.Const(p[3], p[2], p[5]) + + def p_constant(self, p): + """constant : literal + | identifier_wrapped""" + p[0] = p[1] + + def p_identifier_wrapped(self, p): + """identifier_wrapped : identifier""" + p[0] = ('IDENTIFIER', p[1]) + + # TODO(vtl): Make this produce a "wrapped" identifier (probably as an + # |ast.Identifier|, to be added) and get rid of identifier_wrapped. + def p_identifier(self, p): + """identifier : NAME + | NAME DOT identifier""" + p[0] = ''.join(p[1:]) + + def p_literal(self, p): + """literal : int + | float + | TRUE + | FALSE + | DEFAULT + | STRING_LITERAL""" + p[0] = p[1] + + def p_int(self, p): + """int : int_const + | PLUS int_const + | MINUS int_const""" + p[0] = ''.join(p[1:]) + + def p_int_const(self, p): + """int_const : INT_CONST_DEC + | INT_CONST_HEX""" + p[0] = p[1] + + def p_float(self, p): + """float : FLOAT_CONST + | PLUS FLOAT_CONST + | MINUS FLOAT_CONST""" + p[0] = ''.join(p[1:]) + + def p_error(self, e): + if e is None: + # Unexpected EOF. + # TODO(vtl): Can we figure out what's missing? + raise ParseError(self.filename, "Unexpected end of file") + + raise ParseError(self.filename, "Unexpected %r:" % e.value, lineno=e.lineno, + snippet=self._GetSnippet(e.lineno)) + + def _GetSnippet(self, lineno): + return self.source.split('\n')[lineno - 1] + + +def Parse(source, filename): + lexer = Lexer(filename) + parser = Parser(lexer, source, filename) + + lex.lex(object=lexer) + yacc.yacc(module=parser, debug=0, write_tables=0) + + tree = yacc.parse(source) + return tree diff --git a/mojo/public/tools/bindings/pylib/mojom/parse/translate.py b/mojo/public/tools/bindings/pylib/mojom/parse/translate.py new file mode 100644 index 0000000..aad245f --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom/parse/translate.py @@ -0,0 +1,222 @@ +# 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, + '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, + '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): + return _MojomBuilder().Build(tree, name) diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/__init__.py b/mojo/public/tools/bindings/pylib/mojom_tests/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom_tests/__init__.py diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/fileutil_unittest.py b/mojo/public/tools/bindings/pylib/mojom_tests/fileutil_unittest.py new file mode 100644 index 0000000..d56faad --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom_tests/fileutil_unittest.py @@ -0,0 +1,55 @@ +# Copyright 2015 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 imp +import os.path +import shutil +import sys +import tempfile +import unittest + +def _GetDirAbove(dirname): + """Returns the directory "above" this file containing |dirname| (which must + also be "above" this file).""" + path = os.path.abspath(__file__) + while True: + path, tail = os.path.split(path) + assert tail + if tail == dirname: + return path + +try: + imp.find_module("mojom") +except ImportError: + sys.path.append(os.path.join(_GetDirAbove("pylib"), "pylib")) +from mojom import fileutil + + +class FileUtilTest(unittest.TestCase): + + def testEnsureDirectoryExists(self): + """Test that EnsureDirectoryExists fuctions correctly.""" + + temp_dir = tempfile.mkdtemp() + try: + self.assertTrue(os.path.exists(temp_dir)) + + # Directory does not exist, yet. + full = os.path.join(temp_dir, "foo", "bar") + self.assertFalse(os.path.exists(full)) + + # Create the directory. + fileutil.EnsureDirectoryExists(full) + self.assertTrue(os.path.exists(full)) + + # Trying to create it again does not cause an error. + fileutil.EnsureDirectoryExists(full) + self.assertTrue(os.path.exists(full)) + + # Bypass check for directory existence to tickle error handling that + # occurs in response to a race. + fileutil.EnsureDirectoryExists(full, always_try_to_create=True) + self.assertTrue(os.path.exists(full)) + finally: + shutil.rmtree(temp_dir) diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/generate/__init__.py b/mojo/public/tools/bindings/pylib/mojom_tests/generate/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom_tests/generate/__init__.py diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/generate/data_unittest.py b/mojo/public/tools/bindings/pylib/mojom_tests/generate/data_unittest.py new file mode 100644 index 0000000..70c92a3 --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom_tests/generate/data_unittest.py @@ -0,0 +1,156 @@ +# 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. + +import imp +import os.path +import sys +import unittest + +def _GetDirAbove(dirname): + """Returns the directory "above" this file containing |dirname| (which must + also be "above" this file).""" + path = os.path.abspath(__file__) + while True: + path, tail = os.path.split(path) + assert tail + if tail == dirname: + return path + +try: + imp.find_module("mojom") +except ImportError: + sys.path.append(os.path.join(_GetDirAbove("pylib"), "pylib")) +from mojom.generate import data +from mojom.generate import module as mojom + + +class DataTest(unittest.TestCase): + + def testStructDataConversion(self): + """Tests that a struct can be converted from data.""" + module = mojom.Module('test_module', 'test_namespace') + struct_data = { + 'name': 'SomeStruct', + 'enums': [], + 'constants': [], + 'fields': [ + {'name': 'field1', 'kind': 'i32'}, + {'name': 'field2', 'kind': 'i32', 'ordinal': 10}, + {'name': 'field3', 'kind': 'i32', 'default': 15}]} + + struct = data.StructFromData(module, struct_data) + struct.fields = map(lambda field: + data.StructFieldFromData(module, field, struct), struct.fields_data) + self.assertEquals(struct_data, data.StructToData(struct)) + + def testUnionDataConversion(self): + """Tests that a union can be converted from data.""" + module = mojom.Module('test_module', 'test_namespace') + union_data = { + 'name': 'SomeUnion', + 'fields': [ + {'name': 'field1', 'kind': 'i32'}, + {'name': 'field2', 'kind': 'i32', 'ordinal': 10}]} + + union = data.UnionFromData(module, union_data) + union.fields = map(lambda field: + data.UnionFieldFromData(module, field, union), union.fields_data) + self.assertEquals(union_data, data.UnionToData(union)) + + def testImportFromDataNoMissingImports(self): + """Tests that unions, structs, interfaces and enums are imported.""" + module = mojom.Module('test_module', 'test_namespace') + imported_module = mojom.Module('import_module', 'import_namespace') + #TODO(azani): Init values in module.py. + #TODO(azani): Test that values are imported. + imported_module.values = {} + imported_data = {'module' : imported_module} + + + struct = mojom.Struct('TestStruct', module=module) + imported_module.kinds[struct.spec] = struct + + union = mojom.Union('TestUnion', module=module) + imported_module.kinds[union.spec] = union + + interface = mojom.Interface('TestInterface', module=module) + imported_module.kinds[interface.spec] = interface + + enum = mojom.Enum('TestEnum', module=module) + imported_module.kinds[enum.spec] = enum + + data.ImportFromData(module, imported_data) + + # Test that the kind was imported. + self.assertIn(struct.spec, module.kinds) + self.assertEquals(struct.name, module.kinds[struct.spec].name) + + self.assertIn(union.spec, module.kinds) + self.assertEquals(union.name, module.kinds[union.spec].name) + + self.assertIn(interface.spec, module.kinds) + self.assertEquals(interface.name, module.kinds[interface.spec].name) + + self.assertIn(enum.spec, module.kinds) + self.assertEquals(enum.name, module.kinds[enum.spec].name) + + # Test that the imported kind is a copy and not the original. + self.assertIsNot(struct, module.kinds[struct.spec]) + self.assertIsNot(union, module.kinds[union.spec]) + self.assertIsNot(interface, module.kinds[interface.spec]) + self.assertIsNot(enum, module.kinds[enum.spec]) + + def testImportFromDataNoExtraneousImports(self): + """Tests that arrays, maps and interface requests are not imported.""" + module = mojom.Module('test_module', 'test_namespace') + imported_module = mojom.Module('import_module', 'import_namespace') + #TODO(azani): Init values in module.py. + imported_module.values = {} + imported_data = {'module' : imported_module} + + array = mojom.Array(mojom.INT16, length=20) + imported_module.kinds[array.spec] = array + + map_kind = mojom.Map(mojom.INT16, mojom.INT16) + imported_module.kinds[map_kind.spec] = map_kind + + interface = mojom.Interface('TestInterface', module=module) + imported_module.kinds[interface.spec] = interface + + interface_req = mojom.InterfaceRequest(interface) + imported_module.kinds[interface_req.spec] = interface_req + + data.ImportFromData(module, imported_data) + + self.assertNotIn(array.spec, module.kinds) + self.assertNotIn(map_kind.spec, module.kinds) + self.assertNotIn(interface_req.spec, module.kinds) + + def testNonInterfaceAsInterfaceRequest(self): + """Tests that a non-interface cannot be used for interface requests.""" + module = mojom.Module('test_module', 'test_namespace') + interface = mojom.Interface('TestInterface', module=module) + method_dict = { + 'name': 'Foo', + 'parameters': [{'name': 'foo', 'kind': 'r:i32'}], + } + with self.assertRaises(Exception) as e: + data.MethodFromData(module, method_dict, interface) + self.assertEquals(e.exception.__str__(), + 'Interface request requires \'i32\' to be an interface.') + + def testNonInterfaceAsAssociatedInterface(self): + """Tests that a non-interface type cannot be used for associated + interfaces.""" + module = mojom.Module('test_module', 'test_namespace') + interface = mojom.Interface('TestInterface', module=module) + method_dict = { + 'name': 'Foo', + 'parameters': [{'name': 'foo', 'kind': 'asso:i32'}], + } + with self.assertRaises(Exception) as e: + data.MethodFromData(module, method_dict, interface) + self.assertEquals( + e.exception.__str__(), + 'Associated interface requires \'i32\' to be an interface.') diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/generate/generator_unittest.py b/mojo/public/tools/bindings/pylib/mojom_tests/generate/generator_unittest.py new file mode 100644 index 0000000..a684773 --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom_tests/generate/generator_unittest.py @@ -0,0 +1,37 @@ +# 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. + +import imp +import os.path +import sys +import unittest + +def _GetDirAbove(dirname): + """Returns the directory "above" this file containing |dirname| (which must + also be "above" this file).""" + path = os.path.abspath(__file__) + while True: + path, tail = os.path.split(path) + assert tail + if tail == dirname: + return path + +try: + imp.find_module("mojom") +except ImportError: + sys.path.append(os.path.join(_GetDirAbove("pylib"), "pylib")) +from mojom.generate import generator + + +class StringManipulationTest(unittest.TestCase): + """generator contains some string utilities, this tests only those.""" + + def testUnderToCamel(self): + """Tests UnderToCamel which converts underscore_separated to CamelCase.""" + self.assertEquals("CamelCase", generator.UnderToCamel("camel_case")) + self.assertEquals("CamelCase", generator.UnderToCamel("CAMEL_CASE")) + +if __name__ == "__main__": + unittest.main() + diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/generate/module_unittest.py b/mojo/public/tools/bindings/pylib/mojom_tests/generate/module_unittest.py new file mode 100644 index 0000000..75b7cd5 --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom_tests/generate/module_unittest.py @@ -0,0 +1,48 @@ +# 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. + +import imp +import os.path +import sys +import unittest + +def _GetDirAbove(dirname): + """Returns the directory "above" this file containing |dirname| (which must + also be "above" this file).""" + path = os.path.abspath(__file__) + while True: + path, tail = os.path.split(path) + assert tail + if tail == dirname: + return path + +try: + imp.find_module("mojom") +except ImportError: + sys.path.append(os.path.join(_GetDirAbove("pylib"), "pylib")) +from mojom.generate import module as mojom + + +class ModuleTest(unittest.TestCase): + + def testNonInterfaceAsInterfaceRequest(self): + """Tests that a non-interface cannot be used for interface requests.""" + module = mojom.Module('test_module', 'test_namespace') + struct = mojom.Struct('TestStruct', module=module) + with self.assertRaises(Exception) as e: + mojom.InterfaceRequest(struct) + self.assertEquals( + e.exception.__str__(), + 'Interface request requires \'x:TestStruct\' to be an interface.') + + def testNonInterfaceAsAssociatedInterface(self): + """Tests that a non-interface type cannot be used for associated interfaces. + """ + module = mojom.Module('test_module', 'test_namespace') + struct = mojom.Struct('TestStruct', module=module) + with self.assertRaises(Exception) as e: + mojom.AssociatedInterface(struct) + self.assertEquals( + e.exception.__str__(), + 'Associated interface requires \'x:TestStruct\' to be an interface.') diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/generate/pack_unittest.py b/mojo/public/tools/bindings/pylib/mojom_tests/generate/pack_unittest.py new file mode 100644 index 0000000..75f6d51 --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom_tests/generate/pack_unittest.py @@ -0,0 +1,136 @@ +# Copyright 2015 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 imp +import os.path +import sys +import unittest + + +def _GetDirAbove(dirname): + """Returns the directory "above" this file containing |dirname| (which must + also be "above" this file).""" + path = os.path.abspath(__file__) + while True: + path, tail = os.path.split(path) + assert tail + if tail == dirname: + return path + + +try: + imp.find_module("mojom") +except ImportError: + sys.path.append(os.path.join(_GetDirAbove("pylib"), "pylib")) +from mojom.generate import pack +from mojom.generate import module as mojom + + +# TODO(yzshen): Move tests in pack_tests.py here. +class PackTest(unittest.TestCase): + def _CheckPackSequence(self, kinds, fields, offsets): + """Checks the pack order and offsets of a sequence of mojom.Kinds. + + Args: + kinds: A sequence of mojom.Kinds that specify the fields that are to be + created. + fields: The expected order of the resulting fields, with the integer "1" + first. + offsets: The expected order of offsets, with the integer "0" first. + """ + struct = mojom.Struct('test') + index = 1 + for kind in kinds: + struct.AddField('%d' % index, kind) + index += 1 + ps = pack.PackedStruct(struct) + num_fields = len(ps.packed_fields) + self.assertEquals(len(kinds), num_fields) + for i in xrange(num_fields): + self.assertEquals('%d' % fields[i], ps.packed_fields[i].field.name) + self.assertEquals(offsets[i], ps.packed_fields[i].offset) + + def testMinVersion(self): + """Tests that |min_version| is properly set for packed fields.""" + struct = mojom.Struct('test') + struct.AddField('field_2', mojom.BOOL, 2) + struct.AddField('field_0', mojom.INT32, 0) + struct.AddField('field_1', mojom.INT64, 1) + ps = pack.PackedStruct(struct) + + self.assertEquals('field_0', ps.packed_fields[0].field.name) + self.assertEquals('field_2', ps.packed_fields[1].field.name) + self.assertEquals('field_1', ps.packed_fields[2].field.name) + + self.assertEquals(0, ps.packed_fields[0].min_version) + self.assertEquals(0, ps.packed_fields[1].min_version) + self.assertEquals(0, ps.packed_fields[2].min_version) + + struct.fields[0].attributes = {'MinVersion': 1} + ps = pack.PackedStruct(struct) + + self.assertEquals(0, ps.packed_fields[0].min_version) + self.assertEquals(1, ps.packed_fields[1].min_version) + self.assertEquals(0, ps.packed_fields[2].min_version) + + def testGetVersionInfoEmptyStruct(self): + """Tests that pack.GetVersionInfo() never returns an empty list, even for + empty structs. + """ + struct = mojom.Struct('test') + ps = pack.PackedStruct(struct) + + versions = pack.GetVersionInfo(ps) + self.assertEquals(1, len(versions)) + self.assertEquals(0, versions[0].version) + self.assertEquals(0, versions[0].num_fields) + self.assertEquals(8, versions[0].num_bytes) + + def testGetVersionInfoComplexOrder(self): + """Tests pack.GetVersionInfo() using a struct whose definition order, + ordinal order and pack order for fields are all different. + """ + struct = mojom.Struct('test') + struct.AddField('field_3', mojom.BOOL, ordinal=3, + attributes={'MinVersion': 3}) + struct.AddField('field_0', mojom.INT32, ordinal=0) + struct.AddField('field_1', mojom.INT64, ordinal=1, + attributes={'MinVersion': 2}) + struct.AddField('field_2', mojom.INT64, ordinal=2, + attributes={'MinVersion': 3}) + ps = pack.PackedStruct(struct) + + versions = pack.GetVersionInfo(ps) + self.assertEquals(3, len(versions)) + + self.assertEquals(0, versions[0].version) + self.assertEquals(1, versions[0].num_fields) + self.assertEquals(16, versions[0].num_bytes) + + self.assertEquals(2, versions[1].version) + self.assertEquals(2, versions[1].num_fields) + self.assertEquals(24, versions[1].num_bytes) + + self.assertEquals(3, versions[2].version) + self.assertEquals(4, versions[2].num_fields) + self.assertEquals(32, versions[2].num_bytes) + + def testInterfaceAlignment(self): + """Tests that interfaces are aligned on 4-byte boundaries, although the size + of an interface is 8 bytes. + """ + kinds = (mojom.INT32, mojom.Interface('test_interface')) + fields = (1, 2) + offsets = (0, 4) + self._CheckPackSequence(kinds, fields, offsets) + + def testAssociatedInterfaceAlignment(self): + """Tests that associated interfaces are aligned on 4-byte boundaries, + although the size of an associated interface is 8 bytes. + """ + kinds = (mojom.INT32, + mojom.AssociatedInterface(mojom.Interface('test_interface'))) + fields = (1, 2) + offsets = (0, 4) + self._CheckPackSequence(kinds, fields, offsets) diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/parse/__init__.py b/mojo/public/tools/bindings/pylib/mojom_tests/parse/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom_tests/parse/__init__.py diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/parse/ast_unittest.py b/mojo/public/tools/bindings/pylib/mojom_tests/parse/ast_unittest.py new file mode 100644 index 0000000..dd28cdd --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom_tests/parse/ast_unittest.py @@ -0,0 +1,135 @@ +# 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. + +import imp +import os.path +import sys +import unittest + +def _GetDirAbove(dirname): + """Returns the directory "above" this file containing |dirname| (which must + also be "above" this file).""" + path = os.path.abspath(__file__) + while True: + path, tail = os.path.split(path) + assert tail + if tail == dirname: + return path + +try: + imp.find_module("mojom") +except ImportError: + sys.path.append(os.path.join(_GetDirAbove("pylib"), "pylib")) +import mojom.parse.ast as ast + + +class _TestNode(ast.NodeBase): + """Node type for tests.""" + + def __init__(self, value, **kwargs): + super(_TestNode, self).__init__(**kwargs) + self.value = value + + def __eq__(self, other): + return super(_TestNode, self).__eq__(other) and self.value == other.value + + +class _TestNodeList(ast.NodeListBase): + """Node list type for tests.""" + + _list_item_type = _TestNode + + +class ASTTest(unittest.TestCase): + """Tests various AST classes.""" + + def testNodeBase(self): + # Test |__eq__()|; this is only used for testing, where we want to do + # comparison by value and ignore filenames/line numbers (for convenience). + node1 = ast.NodeBase(filename="hello.mojom", lineno=123) + node2 = ast.NodeBase() + self.assertEquals(node1, node2) + self.assertEquals(node2, node1) + + # Check that |__ne__()| just defers to |__eq__()| properly. + self.assertFalse(node1 != node2) + self.assertFalse(node2 != node1) + + # Check that |filename| and |lineno| are set properly (and are None by + # default). + self.assertEquals(node1.filename, "hello.mojom") + self.assertEquals(node1.lineno, 123) + self.assertIsNone(node2.filename) + self.assertIsNone(node2.lineno) + + # |NodeBase|'s |__eq__()| should compare types (and a subclass's |__eq__()| + # should first defer to its superclass's). + node3 = _TestNode(123) + self.assertNotEqual(node1, node3) + self.assertNotEqual(node3, node1) + # Also test |__eq__()| directly. + self.assertFalse(node1 == node3) + self.assertFalse(node3 == node1) + + node4 = _TestNode(123, filename="world.mojom", lineno=123) + self.assertEquals(node4, node3) + node5 = _TestNode(456) + self.assertNotEquals(node5, node4) + + def testNodeListBase(self): + node1 = _TestNode(1, filename="foo.mojom", lineno=1) + # Equal to, but not the same as, |node1|: + node1b = _TestNode(1, filename="foo.mojom", lineno=1) + node2 = _TestNode(2, filename="foo.mojom", lineno=2) + + nodelist1 = _TestNodeList() # Contains: (empty). + self.assertEquals(nodelist1, nodelist1) + self.assertEquals(nodelist1.items, []) + self.assertIsNone(nodelist1.filename) + self.assertIsNone(nodelist1.lineno) + + nodelist2 = _TestNodeList(node1) # Contains: 1. + self.assertEquals(nodelist2, nodelist2) + self.assertEquals(nodelist2.items, [node1]) + self.assertNotEqual(nodelist2, nodelist1) + self.assertEquals(nodelist2.filename, "foo.mojom") + self.assertEquals(nodelist2.lineno, 1) + + nodelist3 = _TestNodeList([node2]) # Contains: 2. + self.assertEquals(nodelist3.items, [node2]) + self.assertNotEqual(nodelist3, nodelist1) + self.assertNotEqual(nodelist3, nodelist2) + self.assertEquals(nodelist3.filename, "foo.mojom") + self.assertEquals(nodelist3.lineno, 2) + + nodelist1.Append(node1b) # Contains: 1. + self.assertEquals(nodelist1.items, [node1]) + self.assertEquals(nodelist1, nodelist2) + self.assertNotEqual(nodelist1, nodelist3) + self.assertEquals(nodelist1.filename, "foo.mojom") + self.assertEquals(nodelist1.lineno, 1) + + nodelist1.Append(node2) # Contains: 1, 2. + self.assertEquals(nodelist1.items, [node1, node2]) + self.assertNotEqual(nodelist1, nodelist2) + self.assertNotEqual(nodelist1, nodelist3) + self.assertEquals(nodelist1.lineno, 1) + + nodelist2.Append(node2) # Contains: 1, 2. + self.assertEquals(nodelist2.items, [node1, node2]) + self.assertEquals(nodelist2, nodelist1) + self.assertNotEqual(nodelist2, nodelist3) + self.assertEquals(nodelist2.lineno, 1) + + nodelist3.Insert(node1) # Contains: 1, 2. + self.assertEquals(nodelist3.items, [node1, node2]) + self.assertEquals(nodelist3, nodelist1) + self.assertEquals(nodelist3, nodelist2) + self.assertEquals(nodelist3.lineno, 1) + + # Test iteration: + i = 1 + for item in nodelist1: + self.assertEquals(item.value, i) + i += 1 diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/parse/lexer_unittest.py b/mojo/public/tools/bindings/pylib/mojom_tests/parse/lexer_unittest.py new file mode 100644 index 0000000..71f70ce --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom_tests/parse/lexer_unittest.py @@ -0,0 +1,192 @@ +# 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. + +import imp +import os.path +import sys +import unittest + +def _GetDirAbove(dirname): + """Returns the directory "above" this file containing |dirname| (which must + also be "above" this file).""" + path = os.path.abspath(__file__) + while True: + path, tail = os.path.split(path) + assert tail + if tail == dirname: + return path + +try: + imp.find_module("ply") +except ImportError: + sys.path.append(os.path.join(_GetDirAbove("public"), "public/third_party")) +from ply import lex + +try: + imp.find_module("mojom") +except ImportError: + sys.path.append(os.path.join(_GetDirAbove("pylib"), "pylib")) +import mojom.parse.lexer + + +# This (monkey-patching LexToken to make comparison value-based) is evil, but +# we'll do it anyway. (I'm pretty sure ply's lexer never cares about comparing +# for object identity.) +def _LexTokenEq(self, other): + return self.type == other.type and self.value == other.value and \ + self.lineno == other.lineno and self.lexpos == other.lexpos +setattr(lex.LexToken, '__eq__', _LexTokenEq) + + +def _MakeLexToken(token_type, value, lineno=1, lexpos=0): + """Makes a LexToken with the given parameters. (Note that lineno is 1-based, + but lexpos is 0-based.)""" + rv = lex.LexToken() + rv.type, rv.value, rv.lineno, rv.lexpos = token_type, value, lineno, lexpos + return rv + + +def _MakeLexTokenForKeyword(keyword, **kwargs): + """Makes a LexToken for the given keyword.""" + return _MakeLexToken(keyword.upper(), keyword.lower(), **kwargs) + + +class LexerTest(unittest.TestCase): + """Tests |mojom.parse.lexer.Lexer|.""" + + def __init__(self, *args, **kwargs): + unittest.TestCase.__init__(self, *args, **kwargs) + # Clone all lexer instances from this one, since making a lexer is slow. + self._zygote_lexer = lex.lex(mojom.parse.lexer.Lexer("my_file.mojom")) + + def testValidKeywords(self): + """Tests valid keywords.""" + self.assertEquals(self._SingleTokenForInput("handle"), + _MakeLexTokenForKeyword("handle")) + self.assertEquals(self._SingleTokenForInput("import"), + _MakeLexTokenForKeyword("import")) + self.assertEquals(self._SingleTokenForInput("module"), + _MakeLexTokenForKeyword("module")) + self.assertEquals(self._SingleTokenForInput("struct"), + _MakeLexTokenForKeyword("struct")) + self.assertEquals(self._SingleTokenForInput("union"), + _MakeLexTokenForKeyword("union")) + self.assertEquals(self._SingleTokenForInput("interface"), + _MakeLexTokenForKeyword("interface")) + self.assertEquals(self._SingleTokenForInput("enum"), + _MakeLexTokenForKeyword("enum")) + self.assertEquals(self._SingleTokenForInput("const"), + _MakeLexTokenForKeyword("const")) + self.assertEquals(self._SingleTokenForInput("true"), + _MakeLexTokenForKeyword("true")) + self.assertEquals(self._SingleTokenForInput("false"), + _MakeLexTokenForKeyword("false")) + self.assertEquals(self._SingleTokenForInput("default"), + _MakeLexTokenForKeyword("default")) + self.assertEquals(self._SingleTokenForInput("array"), + _MakeLexTokenForKeyword("array")) + self.assertEquals(self._SingleTokenForInput("map"), + _MakeLexTokenForKeyword("map")) + self.assertEquals(self._SingleTokenForInput("associated"), + _MakeLexTokenForKeyword("associated")) + + def testValidIdentifiers(self): + """Tests identifiers.""" + self.assertEquals(self._SingleTokenForInput("abcd"), + _MakeLexToken("NAME", "abcd")) + self.assertEquals(self._SingleTokenForInput("AbC_d012_"), + _MakeLexToken("NAME", "AbC_d012_")) + self.assertEquals(self._SingleTokenForInput("_0123"), + _MakeLexToken("NAME", "_0123")) + + def testInvalidIdentifiers(self): + with self.assertRaisesRegexp( + mojom.parse.lexer.LexError, + r"^my_file\.mojom:1: Error: Illegal character '\$'$"): + self._TokensForInput("$abc") + with self.assertRaisesRegexp( + mojom.parse.lexer.LexError, + r"^my_file\.mojom:1: Error: Illegal character '\$'$"): + self._TokensForInput("a$bc") + + def testDecimalIntegerConstants(self): + self.assertEquals(self._SingleTokenForInput("0"), + _MakeLexToken("INT_CONST_DEC", "0")) + self.assertEquals(self._SingleTokenForInput("1"), + _MakeLexToken("INT_CONST_DEC", "1")) + self.assertEquals(self._SingleTokenForInput("123"), + _MakeLexToken("INT_CONST_DEC", "123")) + self.assertEquals(self._SingleTokenForInput("10"), + _MakeLexToken("INT_CONST_DEC", "10")) + + def testValidTokens(self): + """Tests valid tokens (which aren't tested elsewhere).""" + # Keywords tested in |testValidKeywords|. + # NAME tested in |testValidIdentifiers|. + self.assertEquals(self._SingleTokenForInput("@123"), + _MakeLexToken("ORDINAL", "@123")) + self.assertEquals(self._SingleTokenForInput("456"), + _MakeLexToken("INT_CONST_DEC", "456")) + self.assertEquals(self._SingleTokenForInput("0x01aB2eF3"), + _MakeLexToken("INT_CONST_HEX", "0x01aB2eF3")) + self.assertEquals(self._SingleTokenForInput("123.456"), + _MakeLexToken("FLOAT_CONST", "123.456")) + self.assertEquals(self._SingleTokenForInput("\"hello\""), + _MakeLexToken("STRING_LITERAL", "\"hello\"")) + self.assertEquals(self._SingleTokenForInput("+"), + _MakeLexToken("PLUS", "+")) + self.assertEquals(self._SingleTokenForInput("-"), + _MakeLexToken("MINUS", "-")) + self.assertEquals(self._SingleTokenForInput("&"), + _MakeLexToken("AMP", "&")) + self.assertEquals(self._SingleTokenForInput("?"), + _MakeLexToken("QSTN", "?")) + self.assertEquals(self._SingleTokenForInput("="), + _MakeLexToken("EQUALS", "=")) + self.assertEquals(self._SingleTokenForInput("=>"), + _MakeLexToken("RESPONSE", "=>")) + self.assertEquals(self._SingleTokenForInput("("), + _MakeLexToken("LPAREN", "(")) + self.assertEquals(self._SingleTokenForInput(")"), + _MakeLexToken("RPAREN", ")")) + self.assertEquals(self._SingleTokenForInput("["), + _MakeLexToken("LBRACKET", "[")) + self.assertEquals(self._SingleTokenForInput("]"), + _MakeLexToken("RBRACKET", "]")) + self.assertEquals(self._SingleTokenForInput("{"), + _MakeLexToken("LBRACE", "{")) + self.assertEquals(self._SingleTokenForInput("}"), + _MakeLexToken("RBRACE", "}")) + self.assertEquals(self._SingleTokenForInput("<"), + _MakeLexToken("LANGLE", "<")) + self.assertEquals(self._SingleTokenForInput(">"), + _MakeLexToken("RANGLE", ">")) + self.assertEquals(self._SingleTokenForInput(";"), + _MakeLexToken("SEMI", ";")) + self.assertEquals(self._SingleTokenForInput(","), + _MakeLexToken("COMMA", ",")) + self.assertEquals(self._SingleTokenForInput("."), + _MakeLexToken("DOT", ".")) + + def _TokensForInput(self, input_string): + """Gets a list of tokens for the given input string.""" + lexer = self._zygote_lexer.clone() + lexer.input(input_string) + rv = [] + while True: + tok = lexer.token() + if not tok: + return rv + rv.append(tok) + + def _SingleTokenForInput(self, input_string): + """Gets the single token for the given input string. (Raises an exception if + the input string does not result in exactly one token.)""" + toks = self._TokensForInput(input_string) + assert len(toks) == 1 + return toks[0] + + +if __name__ == "__main__": + unittest.main() diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/parse/parser_unittest.py b/mojo/public/tools/bindings/pylib/mojom_tests/parse/parser_unittest.py new file mode 100644 index 0000000..3f4ca87 --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom_tests/parse/parser_unittest.py @@ -0,0 +1,1497 @@ +# 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. + +import imp +import os.path +import sys +import unittest + +def _GetDirAbove(dirname): + """Returns the directory "above" this file containing |dirname| (which must + also be "above" this file).""" + path = os.path.abspath(__file__) + while True: + path, tail = os.path.split(path) + assert tail + if tail == dirname: + return path + +try: + imp.find_module("mojom") +except ImportError: + sys.path.append(os.path.join(_GetDirAbove("pylib"), "pylib")) +import mojom.parse.ast as ast +import mojom.parse.lexer as lexer +import mojom.parse.parser as parser + + +class ParserTest(unittest.TestCase): + """Tests |parser.Parse()|.""" + + def testTrivialValidSource(self): + """Tests a trivial, but valid, .mojom source.""" + + source = """\ + // This is a comment. + + module my_module; + """ + expected = ast.Mojom( + ast.Module(('IDENTIFIER', 'my_module'), None), + ast.ImportList(), + []) + self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) + + def testSourceWithCrLfs(self): + """Tests a .mojom source with CR-LFs instead of LFs.""" + + source = "// This is a comment.\r\n\r\nmodule my_module;\r\n" + expected = ast.Mojom( + ast.Module(('IDENTIFIER', 'my_module'), None), + ast.ImportList(), + []) + self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) + + def testUnexpectedEOF(self): + """Tests a "truncated" .mojom source.""" + + source = """\ + // This is a comment. + + module my_module + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom: Error: Unexpected end of file$"): + parser.Parse(source, "my_file.mojom") + + def testCommentLineNumbers(self): + """Tests that line numbers are correctly tracked when comments are + present.""" + + source1 = """\ + // Isolated C++-style comments. + + // Foo. + asdf1 + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:4: Error: Unexpected 'asdf1':\n *asdf1$"): + parser.Parse(source1, "my_file.mojom") + + source2 = """\ + // Consecutive C++-style comments. + // Foo. + // Bar. + + struct Yada { // Baz. + // Quux. + int32 x; + }; + + asdf2 + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:10: Error: Unexpected 'asdf2':\n *asdf2$"): + parser.Parse(source2, "my_file.mojom") + + source3 = """\ + /* Single-line C-style comments. */ + /* Foobar. */ + + /* Baz. */ + asdf3 + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:5: Error: Unexpected 'asdf3':\n *asdf3$"): + parser.Parse(source3, "my_file.mojom") + + source4 = """\ + /* Multi-line C-style comments. + */ + /* + Foo. + Bar. + */ + + /* Baz + Quux. */ + asdf4 + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:10: Error: Unexpected 'asdf4':\n *asdf4$"): + parser.Parse(source4, "my_file.mojom") + + + def testSimpleStruct(self): + """Tests a simple .mojom source that just defines a struct.""" + + source = """\ + module my_module; + + struct MyStruct { + int32 a; + double b; + }; + """ + expected = ast.Mojom( + ast.Module(('IDENTIFIER', 'my_module'), None), + ast.ImportList(), + [ast.Struct( + 'MyStruct', + None, + ast.StructBody( + [ast.StructField('a', None, None, 'int32', None), + ast.StructField('b', None, None, 'double', None)]))]) + self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) + + def testSimpleStructWithoutModule(self): + """Tests a simple struct without an explict module statement.""" + + source = """\ + struct MyStruct { + int32 a; + double b; + }; + """ + expected = ast.Mojom( + None, + ast.ImportList(), + [ast.Struct( + 'MyStruct', + None, + ast.StructBody( + [ast.StructField('a', None, None, 'int32', None), + ast.StructField('b', None, None, 'double', None)]))]) + self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) + + def testValidStructDefinitions(self): + """Tests all types of definitions that can occur in a struct.""" + + source = """\ + struct MyStruct { + enum MyEnum { VALUE }; + const double kMyConst = 1.23; + int32 a; + SomeOtherStruct b; // Invalidity detected at another stage. + }; + """ + expected = ast.Mojom( + None, + ast.ImportList(), + [ast.Struct( + 'MyStruct', + None, + ast.StructBody( + [ast.Enum('MyEnum', + None, + ast.EnumValueList( + ast.EnumValue('VALUE', None, None))), + ast.Const('kMyConst', 'double', '1.23'), + ast.StructField('a', None, None, 'int32', None), + ast.StructField('b', None, None, 'SomeOtherStruct', None)]))]) + self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) + + def testInvalidStructDefinitions(self): + """Tests that definitions that aren't allowed in a struct are correctly + detected.""" + + source1 = """\ + struct MyStruct { + MyMethod(int32 a); + }; + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:2: Error: Unexpected '\(':\n" + r" *MyMethod\(int32 a\);$"): + parser.Parse(source1, "my_file.mojom") + + source2 = """\ + struct MyStruct { + struct MyInnerStruct { + int32 a; + }; + }; + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:2: Error: Unexpected 'struct':\n" + r" *struct MyInnerStruct {$"): + parser.Parse(source2, "my_file.mojom") + + source3 = """\ + struct MyStruct { + interface MyInterface { + MyMethod(int32 a); + }; + }; + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:2: Error: Unexpected 'interface':\n" + r" *interface MyInterface {$"): + parser.Parse(source3, "my_file.mojom") + + def testMissingModuleName(self): + """Tests an (invalid) .mojom with a missing module name.""" + + source1 = """\ + // Missing module name. + module ; + struct MyStruct { + int32 a; + }; + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:2: Error: Unexpected ';':\n *module ;$"): + parser.Parse(source1, "my_file.mojom") + + # Another similar case, but make sure that line-number tracking/reporting + # is correct. + source2 = """\ + module + // This line intentionally left unblank. + + struct MyStruct { + int32 a; + }; + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:4: Error: Unexpected 'struct':\n" + r" *struct MyStruct {$"): + parser.Parse(source2, "my_file.mojom") + + def testMultipleModuleStatements(self): + """Tests an (invalid) .mojom with multiple module statements.""" + + source = """\ + module foo; + module bar; + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:2: Error: Multiple \"module\" statements not " + r"allowed:\n *module bar;$"): + parser.Parse(source, "my_file.mojom") + + def testModuleStatementAfterImport(self): + """Tests an (invalid) .mojom with a module statement after an import.""" + + source = """\ + import "foo.mojom"; + module foo; + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:2: Error: \"module\" statements must precede imports " + r"and definitions:\n *module foo;$"): + parser.Parse(source, "my_file.mojom") + + def testModuleStatementAfterDefinition(self): + """Tests an (invalid) .mojom with a module statement after a definition.""" + + source = """\ + struct MyStruct { + int32 a; + }; + module foo; + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:4: Error: \"module\" statements must precede imports " + r"and definitions:\n *module foo;$"): + parser.Parse(source, "my_file.mojom") + + def testImportStatementAfterDefinition(self): + """Tests an (invalid) .mojom with an import statement after a definition.""" + + source = """\ + struct MyStruct { + int32 a; + }; + import "foo.mojom"; + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:4: Error: \"import\" statements must precede " + r"definitions:\n *import \"foo.mojom\";$"): + parser.Parse(source, "my_file.mojom") + + def testEnums(self): + """Tests that enum statements are correctly parsed.""" + + source = """\ + module my_module; + enum MyEnum1 { VALUE1, VALUE2 }; // No trailing comma. + enum MyEnum2 { + VALUE1 = -1, + VALUE2 = 0, + VALUE3 = + 987, // Check that space is allowed. + VALUE4 = 0xAF12, + VALUE5 = -0x09bcd, + VALUE6 = VALUE5, + VALUE7, // Leave trailing comma. + }; + """ + expected = ast.Mojom( + ast.Module(('IDENTIFIER', 'my_module'), None), + ast.ImportList(), + [ast.Enum( + 'MyEnum1', + None, + ast.EnumValueList([ast.EnumValue('VALUE1', None, None), + ast.EnumValue('VALUE2', None, None)])), + ast.Enum( + 'MyEnum2', + None, + ast.EnumValueList([ast.EnumValue('VALUE1', None, '-1'), + ast.EnumValue('VALUE2', None, '0'), + ast.EnumValue('VALUE3', None, '+987'), + ast.EnumValue('VALUE4', None, '0xAF12'), + ast.EnumValue('VALUE5', None, '-0x09bcd'), + ast.EnumValue('VALUE6', None, ('IDENTIFIER', + 'VALUE5')), + ast.EnumValue('VALUE7', None, None)]))]) + self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) + + def testInvalidEnumInitializers(self): + """Tests that invalid enum initializers are correctly detected.""" + + # No values. + source1 = """\ + enum MyEnum { + }; + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:2: Error: Unexpected '}':\n" + r" *};$"): + parser.Parse(source1, "my_file.mojom") + + # Floating point value. + source2 = "enum MyEnum { VALUE = 0.123 };" + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:1: Error: Unexpected '0\.123':\n" + r"enum MyEnum { VALUE = 0\.123 };$"): + parser.Parse(source2, "my_file.mojom") + + # Boolean value. + source2 = "enum MyEnum { VALUE = true };" + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:1: Error: Unexpected 'true':\n" + r"enum MyEnum { VALUE = true };$"): + parser.Parse(source2, "my_file.mojom") + + def testConsts(self): + """Tests some constants and struct members initialized with them.""" + + source = """\ + module my_module; + + struct MyStruct { + const int8 kNumber = -1; + int8 number@0 = kNumber; + }; + """ + expected = ast.Mojom( + ast.Module(('IDENTIFIER', 'my_module'), None), + ast.ImportList(), + [ast.Struct( + 'MyStruct', None, + ast.StructBody( + [ast.Const('kNumber', 'int8', '-1'), + ast.StructField('number', None, ast.Ordinal(0), 'int8', + ('IDENTIFIER', 'kNumber'))]))]) + self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) + + def testNoConditionals(self): + """Tests that ?: is not allowed.""" + + source = """\ + module my_module; + + enum MyEnum { + MY_ENUM_1 = 1 ? 2 : 3 + }; + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:4: Error: Unexpected '\?':\n" + r" *MY_ENUM_1 = 1 \? 2 : 3$"): + parser.Parse(source, "my_file.mojom") + + def testSimpleOrdinals(self): + """Tests that (valid) ordinal values are scanned correctly.""" + + source = """\ + module my_module; + + // This isn't actually valid .mojom, but the problem (missing ordinals) + // should be handled at a different level. + struct MyStruct { + int32 a0@0; + int32 a1@1; + int32 a2@2; + int32 a9@9; + int32 a10 @10; + int32 a11 @11; + int32 a29 @29; + int32 a1234567890 @1234567890; + }; + """ + expected = ast.Mojom( + ast.Module(('IDENTIFIER', 'my_module'), None), + ast.ImportList(), + [ast.Struct( + 'MyStruct', + None, + ast.StructBody( + [ast.StructField('a0', None, ast.Ordinal(0), 'int32', None), + ast.StructField('a1', None, ast.Ordinal(1), 'int32', None), + ast.StructField('a2', None, ast.Ordinal(2), 'int32', None), + ast.StructField('a9', None, ast.Ordinal(9), 'int32', None), + ast.StructField('a10', None, ast.Ordinal(10), 'int32', None), + ast.StructField('a11', None, ast.Ordinal(11), 'int32', None), + ast.StructField('a29', None, ast.Ordinal(29), 'int32', None), + ast.StructField('a1234567890', None, ast.Ordinal(1234567890), + 'int32', None)]))]) + self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) + + def testInvalidOrdinals(self): + """Tests that (lexically) invalid ordinals are correctly detected.""" + + source1 = """\ + module my_module; + + struct MyStruct { + int32 a_missing@; + }; + """ + with self.assertRaisesRegexp( + lexer.LexError, + r"^my_file\.mojom:4: Error: Missing ordinal value$"): + parser.Parse(source1, "my_file.mojom") + + source2 = """\ + module my_module; + + struct MyStruct { + int32 a_octal@01; + }; + """ + with self.assertRaisesRegexp( + lexer.LexError, + r"^my_file\.mojom:4: Error: " + r"Octal and hexadecimal ordinal values not allowed$"): + parser.Parse(source2, "my_file.mojom") + + source3 = """\ + module my_module; struct MyStruct { int32 a_invalid_octal@08; }; + """ + with self.assertRaisesRegexp( + lexer.LexError, + r"^my_file\.mojom:1: Error: " + r"Octal and hexadecimal ordinal values not allowed$"): + parser.Parse(source3, "my_file.mojom") + + source4 = "module my_module; struct MyStruct { int32 a_hex@0x1aB9; };" + with self.assertRaisesRegexp( + lexer.LexError, + r"^my_file\.mojom:1: Error: " + r"Octal and hexadecimal ordinal values not allowed$"): + parser.Parse(source4, "my_file.mojom") + + source5 = "module my_module; struct MyStruct { int32 a_hex@0X0; };" + with self.assertRaisesRegexp( + lexer.LexError, + r"^my_file\.mojom:1: Error: " + r"Octal and hexadecimal ordinal values not allowed$"): + parser.Parse(source5, "my_file.mojom") + + source6 = """\ + struct MyStruct { + int32 a_too_big@999999999999; + }; + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:2: Error: " + r"Ordinal value 999999999999 too large:\n" + r" *int32 a_too_big@999999999999;$"): + parser.Parse(source6, "my_file.mojom") + + def testNestedNamespace(self): + """Tests that "nested" namespaces work.""" + + source = """\ + module my.mod; + + struct MyStruct { + int32 a; + }; + """ + expected = ast.Mojom( + ast.Module(('IDENTIFIER', 'my.mod'), None), + ast.ImportList(), + [ast.Struct( + 'MyStruct', + None, + ast.StructBody(ast.StructField('a', None, None, 'int32', None)))]) + self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) + + def testValidHandleTypes(self): + """Tests (valid) handle types.""" + + source = """\ + struct MyStruct { + handle a; + handle<data_pipe_consumer> b; + handle <data_pipe_producer> c; + handle < message_pipe > d; + handle + < shared_buffer + > e; + }; + """ + expected = ast.Mojom( + None, + ast.ImportList(), + [ast.Struct( + 'MyStruct', + None, + ast.StructBody( + [ast.StructField('a', None, None, 'handle', None), + ast.StructField('b', None, None, 'handle<data_pipe_consumer>', + None), + ast.StructField('c', None, None, 'handle<data_pipe_producer>', + None), + ast.StructField('d', None, None, 'handle<message_pipe>', None), + ast.StructField('e', None, None, 'handle<shared_buffer>', + None)]))]) + self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) + + def testInvalidHandleType(self): + """Tests an invalid (unknown) handle type.""" + + source = """\ + struct MyStruct { + handle<wtf_is_this> foo; + }; + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:2: Error: " + r"Invalid handle type 'wtf_is_this':\n" + r" *handle<wtf_is_this> foo;$"): + parser.Parse(source, "my_file.mojom") + + def testValidDefaultValues(self): + """Tests default values that are valid (to the parser).""" + + source = """\ + struct MyStruct { + int16 a0 = 0; + uint16 a1 = 0x0; + uint16 a2 = 0x00; + uint16 a3 = 0x01; + uint16 a4 = 0xcd; + int32 a5 = 12345; + int64 a6 = -12345; + int64 a7 = +12345; + uint32 a8 = 0x12cd3; + uint32 a9 = -0x12cD3; + uint32 a10 = +0x12CD3; + bool a11 = true; + bool a12 = false; + float a13 = 1.2345; + float a14 = -1.2345; + float a15 = +1.2345; + float a16 = 123.; + float a17 = .123; + double a18 = 1.23E10; + double a19 = 1.E-10; + double a20 = .5E+10; + double a21 = -1.23E10; + double a22 = +.123E10; + }; + """ + expected = ast.Mojom( + None, + ast.ImportList(), + [ast.Struct( + 'MyStruct', + None, + ast.StructBody( + [ast.StructField('a0', None, None, 'int16', '0'), + ast.StructField('a1', None, None, 'uint16', '0x0'), + ast.StructField('a2', None, None, 'uint16', '0x00'), + ast.StructField('a3', None, None, 'uint16', '0x01'), + ast.StructField('a4', None, None, 'uint16', '0xcd'), + ast.StructField('a5' , None, None, 'int32', '12345'), + ast.StructField('a6', None, None, 'int64', '-12345'), + ast.StructField('a7', None, None, 'int64', '+12345'), + ast.StructField('a8', None, None, 'uint32', '0x12cd3'), + ast.StructField('a9', None, None, 'uint32', '-0x12cD3'), + ast.StructField('a10', None, None, 'uint32', '+0x12CD3'), + ast.StructField('a11', None, None, 'bool', 'true'), + ast.StructField('a12', None, None, 'bool', 'false'), + ast.StructField('a13', None, None, 'float', '1.2345'), + ast.StructField('a14', None, None, 'float', '-1.2345'), + ast.StructField('a15', None, None, 'float', '+1.2345'), + ast.StructField('a16', None, None, 'float', '123.'), + ast.StructField('a17', None, None, 'float', '.123'), + ast.StructField('a18', None, None, 'double', '1.23E10'), + ast.StructField('a19', None, None, 'double', '1.E-10'), + ast.StructField('a20', None, None, 'double', '.5E+10'), + ast.StructField('a21', None, None, 'double', '-1.23E10'), + ast.StructField('a22', None, None, 'double', '+.123E10')]))]) + self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) + + def testValidFixedSizeArray(self): + """Tests parsing a fixed size array.""" + + source = """\ + struct MyStruct { + array<int32> normal_array; + array<int32, 1> fixed_size_array_one_entry; + array<int32, 10> fixed_size_array_ten_entries; + array<array<array<int32, 1>>, 2> nested_arrays; + }; + """ + expected = ast.Mojom( + None, + ast.ImportList(), + [ast.Struct( + 'MyStruct', + None, + ast.StructBody( + [ast.StructField('normal_array', None, None, 'int32[]', None), + ast.StructField('fixed_size_array_one_entry', None, None, + 'int32[1]', None), + ast.StructField('fixed_size_array_ten_entries', None, None, + 'int32[10]', None), + ast.StructField('nested_arrays', None, None, + 'int32[1][][2]', None)]))]) + self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) + + def testValidNestedArray(self): + """Tests parsing a nested array.""" + + source = "struct MyStruct { array<array<int32>> nested_array; };" + expected = ast.Mojom( + None, + ast.ImportList(), + [ast.Struct( + 'MyStruct', + None, + ast.StructBody( + ast.StructField('nested_array', None, None, 'int32[][]', + None)))]) + self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) + + def testInvalidFixedArraySize(self): + """Tests that invalid fixed array bounds are correctly detected.""" + + source1 = """\ + struct MyStruct { + array<int32, 0> zero_size_array; + }; + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:2: Error: Fixed array size 0 invalid:\n" + r" *array<int32, 0> zero_size_array;$"): + parser.Parse(source1, "my_file.mojom") + + source2 = """\ + struct MyStruct { + array<int32, 999999999999> too_big_array; + }; + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:2: Error: Fixed array size 999999999999 invalid:\n" + r" *array<int32, 999999999999> too_big_array;$"): + parser.Parse(source2, "my_file.mojom") + + source3 = """\ + struct MyStruct { + array<int32, abcdefg> not_a_number; + }; + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:2: Error: Unexpected 'abcdefg':\n" + r" *array<int32, abcdefg> not_a_number;"): + parser.Parse(source3, "my_file.mojom") + + def testValidAssociativeArrays(self): + """Tests that we can parse valid associative array structures.""" + + source1 = "struct MyStruct { map<string, uint8> data; };" + expected1 = ast.Mojom( + None, + ast.ImportList(), + [ast.Struct( + 'MyStruct', + None, + ast.StructBody( + [ast.StructField('data', None, None, 'uint8{string}', None)]))]) + self.assertEquals(parser.Parse(source1, "my_file.mojom"), expected1) + + source2 = "interface MyInterface { MyMethod(map<string, uint8> a); };" + expected2 = ast.Mojom( + None, + ast.ImportList(), + [ast.Interface( + 'MyInterface', + None, + ast.InterfaceBody( + ast.Method( + 'MyMethod', + None, + None, + ast.ParameterList( + ast.Parameter('a', None, None, 'uint8{string}')), + None)))]) + self.assertEquals(parser.Parse(source2, "my_file.mojom"), expected2) + + source3 = "struct MyStruct { map<string, array<uint8>> data; };" + expected3 = ast.Mojom( + None, + ast.ImportList(), + [ast.Struct( + 'MyStruct', + None, + ast.StructBody( + [ast.StructField('data', None, None, 'uint8[]{string}', + None)]))]) + self.assertEquals(parser.Parse(source3, "my_file.mojom"), expected3) + + def testValidMethod(self): + """Tests parsing method declarations.""" + + source1 = "interface MyInterface { MyMethod(int32 a); };" + expected1 = ast.Mojom( + None, + ast.ImportList(), + [ast.Interface( + 'MyInterface', + None, + ast.InterfaceBody( + ast.Method( + 'MyMethod', + None, + None, + ast.ParameterList(ast.Parameter('a', None, None, 'int32')), + None)))]) + self.assertEquals(parser.Parse(source1, "my_file.mojom"), expected1) + + source2 = """\ + interface MyInterface { + MyMethod1@0(int32 a@0, int64 b@1); + MyMethod2@1() => (); + }; + """ + expected2 = ast.Mojom( + None, + ast.ImportList(), + [ast.Interface( + 'MyInterface', + None, + ast.InterfaceBody( + [ast.Method( + 'MyMethod1', + None, + ast.Ordinal(0), + ast.ParameterList([ast.Parameter('a', None, ast.Ordinal(0), + 'int32'), + ast.Parameter('b', None, ast.Ordinal(1), + 'int64')]), + None), + ast.Method( + 'MyMethod2', + None, + ast.Ordinal(1), + ast.ParameterList(), + ast.ParameterList())]))]) + self.assertEquals(parser.Parse(source2, "my_file.mojom"), expected2) + + source3 = """\ + interface MyInterface { + MyMethod(string a) => (int32 a, bool b); + }; + """ + expected3 = ast.Mojom( + None, + ast.ImportList(), + [ast.Interface( + 'MyInterface', + None, + ast.InterfaceBody( + ast.Method( + 'MyMethod', + None, + None, + ast.ParameterList(ast.Parameter('a', None, None, 'string')), + ast.ParameterList([ast.Parameter('a', None, None, 'int32'), + ast.Parameter('b', None, None, + 'bool')]))))]) + self.assertEquals(parser.Parse(source3, "my_file.mojom"), expected3) + + def testInvalidMethods(self): + """Tests that invalid method declarations are correctly detected.""" + + # No trailing commas. + source1 = """\ + interface MyInterface { + MyMethod(string a,); + }; + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:2: Error: Unexpected '\)':\n" + r" *MyMethod\(string a,\);$"): + parser.Parse(source1, "my_file.mojom") + + # No leading commas. + source2 = """\ + interface MyInterface { + MyMethod(, string a); + }; + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:2: Error: Unexpected ',':\n" + r" *MyMethod\(, string a\);$"): + parser.Parse(source2, "my_file.mojom") + + def testValidInterfaceDefinitions(self): + """Tests all types of definitions that can occur in an interface.""" + + source = """\ + interface MyInterface { + enum MyEnum { VALUE }; + const int32 kMyConst = 123; + MyMethod(int32 x) => (MyEnum y); + }; + """ + expected = ast.Mojom( + None, + ast.ImportList(), + [ast.Interface( + 'MyInterface', + None, + ast.InterfaceBody( + [ast.Enum('MyEnum', + None, + ast.EnumValueList( + ast.EnumValue('VALUE', None, None))), + ast.Const('kMyConst', 'int32', '123'), + ast.Method( + 'MyMethod', + None, + None, + ast.ParameterList(ast.Parameter('x', None, None, 'int32')), + ast.ParameterList(ast.Parameter('y', None, None, + 'MyEnum')))]))]) + self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) + + def testInvalidInterfaceDefinitions(self): + """Tests that definitions that aren't allowed in an interface are correctly + detected.""" + + source1 = """\ + interface MyInterface { + struct MyStruct { + int32 a; + }; + }; + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:2: Error: Unexpected 'struct':\n" + r" *struct MyStruct {$"): + parser.Parse(source1, "my_file.mojom") + + source2 = """\ + interface MyInterface { + interface MyInnerInterface { + MyMethod(int32 x); + }; + }; + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:2: Error: Unexpected 'interface':\n" + r" *interface MyInnerInterface {$"): + parser.Parse(source2, "my_file.mojom") + + source3 = """\ + interface MyInterface { + int32 my_field; + }; + """ + # The parser thinks that "int32" is a plausible name for a method, so it's + # "my_field" that gives it away. + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:2: Error: Unexpected 'my_field':\n" + r" *int32 my_field;$"): + parser.Parse(source3, "my_file.mojom") + + def testValidAttributes(self): + """Tests parsing attributes (and attribute lists).""" + + # Note: We use structs because they have (optional) attribute lists. + + # Empty attribute list. + source1 = "[] struct MyStruct {};" + expected1 = ast.Mojom( + None, + ast.ImportList(), + [ast.Struct('MyStruct', ast.AttributeList(), ast.StructBody())]) + self.assertEquals(parser.Parse(source1, "my_file.mojom"), expected1) + + # One-element attribute list, with name value. + source2 = "[MyAttribute=MyName] struct MyStruct {};" + expected2 = ast.Mojom( + None, + ast.ImportList(), + [ast.Struct( + 'MyStruct', + ast.AttributeList(ast.Attribute("MyAttribute", "MyName")), + ast.StructBody())]) + self.assertEquals(parser.Parse(source2, "my_file.mojom"), expected2) + + # Two-element attribute list, with one string value and one integer value. + source3 = "[MyAttribute1 = \"hello\", MyAttribute2 = 5] struct MyStruct {};" + expected3 = ast.Mojom( + None, + ast.ImportList(), + [ast.Struct( + 'MyStruct', + ast.AttributeList([ast.Attribute("MyAttribute1", "hello"), + ast.Attribute("MyAttribute2", 5)]), + ast.StructBody())]) + self.assertEquals(parser.Parse(source3, "my_file.mojom"), expected3) + + # Various places that attribute list is allowed. + source4 = """\ + [Attr0=0] module my_module; + + [Attr1=1] struct MyStruct { + [Attr2=2] int32 a; + }; + [Attr3=3] union MyUnion { + [Attr4=4] int32 a; + }; + [Attr5=5] enum MyEnum { + [Attr6=6] a + }; + [Attr7=7] interface MyInterface { + [Attr8=8] MyMethod([Attr9=9] int32 a) => ([Attr10=10] bool b); + }; + """ + expected4 = ast.Mojom( + ast.Module(('IDENTIFIER', 'my_module'), + ast.AttributeList([ast.Attribute("Attr0", 0)])), + ast.ImportList(), + [ast.Struct( + 'MyStruct', + ast.AttributeList(ast.Attribute("Attr1", 1)), + ast.StructBody( + ast.StructField( + 'a', ast.AttributeList([ast.Attribute("Attr2", 2)]), + None, 'int32', None))), + ast.Union( + 'MyUnion', + ast.AttributeList(ast.Attribute("Attr3", 3)), + ast.UnionBody( + ast.UnionField( + 'a', ast.AttributeList([ast.Attribute("Attr4", 4)]), None, + 'int32'))), + ast.Enum( + 'MyEnum', + ast.AttributeList(ast.Attribute("Attr5", 5)), + ast.EnumValueList( + ast.EnumValue( + 'VALUE', ast.AttributeList([ast.Attribute("Attr6", 6)]), + None))), + ast.Interface( + 'MyInterface', + ast.AttributeList(ast.Attribute("Attr7", 7)), + ast.InterfaceBody( + ast.Method( + 'MyMethod', + ast.AttributeList(ast.Attribute("Attr8", 8)), + None, + ast.ParameterList( + ast.Parameter( + 'a', ast.AttributeList([ast.Attribute("Attr9", 9)]), + None, 'int32')), + ast.ParameterList( + ast.Parameter( + 'b', + ast.AttributeList([ast.Attribute("Attr10", 10)]), + None, 'bool')))))]) + self.assertEquals(parser.Parse(source4, "my_file.mojom"), expected4) + + # TODO(vtl): Boolean attributes don't work yet. (In fact, we just |eval()| + # literal (non-name) values, which is extremely dubious.) + + def testInvalidAttributes(self): + """Tests that invalid attributes and attribute lists are correctly + detected.""" + + # Trailing commas not allowed. + source1 = "[MyAttribute=MyName,] struct MyStruct {};" + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:1: Error: Unexpected '\]':\n" + r"\[MyAttribute=MyName,\] struct MyStruct {};$"): + parser.Parse(source1, "my_file.mojom") + + # Missing value. + source2 = "[MyAttribute=] struct MyStruct {};" + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:1: Error: Unexpected '\]':\n" + r"\[MyAttribute=\] struct MyStruct {};$"): + parser.Parse(source2, "my_file.mojom") + + # Missing key. + source3 = "[=MyName] struct MyStruct {};" + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:1: Error: Unexpected '=':\n" + r"\[=MyName\] struct MyStruct {};$"): + parser.Parse(source3, "my_file.mojom") + + def testValidImports(self): + """Tests parsing import statements.""" + + # One import (no module statement). + source1 = "import \"somedir/my.mojom\";" + expected1 = ast.Mojom( + None, + ast.ImportList(ast.Import("somedir/my.mojom")), + []) + self.assertEquals(parser.Parse(source1, "my_file.mojom"), expected1) + + # Two imports (no module statement). + source2 = """\ + import "somedir/my1.mojom"; + import "somedir/my2.mojom"; + """ + expected2 = ast.Mojom( + None, + ast.ImportList([ast.Import("somedir/my1.mojom"), + ast.Import("somedir/my2.mojom")]), + []) + self.assertEquals(parser.Parse(source2, "my_file.mojom"), expected2) + + # Imports with module statement. + source3 = """\ + module my_module; + import "somedir/my1.mojom"; + import "somedir/my2.mojom"; + """ + expected3 = ast.Mojom( + ast.Module(('IDENTIFIER', 'my_module'), None), + ast.ImportList([ast.Import("somedir/my1.mojom"), + ast.Import("somedir/my2.mojom")]), + []) + self.assertEquals(parser.Parse(source3, "my_file.mojom"), expected3) + + def testInvalidImports(self): + """Tests that invalid import statements are correctly detected.""" + + source1 = """\ + // Make the error occur on line 2. + import invalid + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:2: Error: Unexpected 'invalid':\n" + r" *import invalid$"): + parser.Parse(source1, "my_file.mojom") + + source2 = """\ + import // Missing string. + struct MyStruct { + int32 a; + }; + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:2: Error: Unexpected 'struct':\n" + r" *struct MyStruct {$"): + parser.Parse(source2, "my_file.mojom") + + source3 = """\ + import "foo.mojom" // Missing semicolon. + struct MyStruct { + int32 a; + }; + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:2: Error: Unexpected 'struct':\n" + r" *struct MyStruct {$"): + parser.Parse(source3, "my_file.mojom") + + def testValidNullableTypes(self): + """Tests parsing nullable types.""" + + source = """\ + struct MyStruct { + int32? a; // This is actually invalid, but handled at a different + // level. + string? b; + array<int32> ? c; + array<string ? > ? d; + array<array<int32>?>? e; + array<int32, 1>? f; + array<string?, 1>? g; + some_struct? h; + handle? i; + handle<data_pipe_consumer>? j; + handle<data_pipe_producer>? k; + handle<message_pipe>? l; + handle<shared_buffer>? m; + some_interface&? n; + }; + """ + expected = ast.Mojom( + None, + ast.ImportList(), + [ast.Struct( + 'MyStruct', + None, + ast.StructBody( + [ast.StructField('a', None, None,'int32?', None), + ast.StructField('b', None, None,'string?', None), + ast.StructField('c', None, None,'int32[]?', None), + ast.StructField('d', None, None,'string?[]?', None), + ast.StructField('e', None, None,'int32[]?[]?', None), + ast.StructField('f', None, None,'int32[1]?', None), + ast.StructField('g', None, None,'string?[1]?', None), + ast.StructField('h', None, None,'some_struct?', None), + ast.StructField('i', None, None,'handle?', None), + ast.StructField('j', None, None,'handle<data_pipe_consumer>?', + None), + ast.StructField('k', None, None,'handle<data_pipe_producer>?', + None), + ast.StructField('l', None, None,'handle<message_pipe>?', None), + ast.StructField('m', None, None,'handle<shared_buffer>?', + None), + ast.StructField('n', None, None,'some_interface&?', None)]))]) + self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) + + def testInvalidNullableTypes(self): + """Tests that invalid nullable types are correctly detected.""" + source1 = """\ + struct MyStruct { + string?? a; + }; + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:2: Error: Unexpected '\?':\n" + r" *string\?\? a;$"): + parser.Parse(source1, "my_file.mojom") + + source2 = """\ + struct MyStruct { + handle?<data_pipe_consumer> a; + }; + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:2: Error: Unexpected '<':\n" + r" *handle\?<data_pipe_consumer> a;$"): + parser.Parse(source2, "my_file.mojom") + + source3 = """\ + struct MyStruct { + some_interface?& a; + }; + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:2: Error: Unexpected '&':\n" + r" *some_interface\?& a;$"): + parser.Parse(source3, "my_file.mojom") + + def testSimpleUnion(self): + """Tests a simple .mojom source that just defines a union.""" + source = """\ + module my_module; + + union MyUnion { + int32 a; + double b; + }; + """ + expected = ast.Mojom( + ast.Module(('IDENTIFIER', 'my_module'), None), + ast.ImportList(), + [ast.Union( + 'MyUnion', + None, + ast.UnionBody([ + ast.UnionField('a', None, None, 'int32'), + ast.UnionField('b', None, None, 'double') + ]))]) + actual = parser.Parse(source, "my_file.mojom") + self.assertEquals(actual, expected) + + def testUnionWithOrdinals(self): + """Test that ordinals are assigned to fields.""" + source = """\ + module my_module; + + union MyUnion { + int32 a @10; + double b @30; + }; + """ + expected = ast.Mojom( + ast.Module(('IDENTIFIER', 'my_module'), None), + ast.ImportList(), + [ast.Union( + 'MyUnion', + None, + ast.UnionBody([ + ast.UnionField('a', None, ast.Ordinal(10), 'int32'), + ast.UnionField('b', None, ast.Ordinal(30), 'double') + ]))]) + actual = parser.Parse(source, "my_file.mojom") + self.assertEquals(actual, expected) + + def testUnionWithStructMembers(self): + """Test that struct members are accepted.""" + source = """\ + module my_module; + + union MyUnion { + SomeStruct s; + }; + """ + expected = ast.Mojom( + ast.Module(('IDENTIFIER', 'my_module'), None), + ast.ImportList(), + [ast.Union( + 'MyUnion', + None, + ast.UnionBody([ + ast.UnionField('s', None, None, 'SomeStruct') + ]))]) + actual = parser.Parse(source, "my_file.mojom") + self.assertEquals(actual, expected) + + def testUnionWithArrayMember(self): + """Test that array members are accepted.""" + source = """\ + module my_module; + + union MyUnion { + array<int32> a; + }; + """ + expected = ast.Mojom( + ast.Module(('IDENTIFIER', 'my_module'), None), + ast.ImportList(), + [ast.Union( + 'MyUnion', + None, + ast.UnionBody([ + ast.UnionField('a', None, None, 'int32[]') + ]))]) + actual = parser.Parse(source, "my_file.mojom") + self.assertEquals(actual, expected) + + def testUnionWithMapMember(self): + """Test that map members are accepted.""" + source = """\ + module my_module; + + union MyUnion { + map<int32, string> m; + }; + """ + expected = ast.Mojom( + ast.Module(('IDENTIFIER', 'my_module'), None), + ast.ImportList(), + [ast.Union( + 'MyUnion', + None, + ast.UnionBody([ + ast.UnionField('m', None, None, 'string{int32}') + ]))]) + actual = parser.Parse(source, "my_file.mojom") + self.assertEquals(actual, expected) + + def testUnionDisallowNestedStruct(self): + """Tests that structs cannot be nested in unions.""" + source = """\ + module my_module; + + union MyUnion { + struct MyStruct { + int32 a; + }; + }; + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:4: Error: Unexpected 'struct':\n" + r" *struct MyStruct {$"): + parser.Parse(source, "my_file.mojom") + + def testUnionDisallowNestedInterfaces(self): + """Tests that interfaces cannot be nested in unions.""" + source = """\ + module my_module; + + union MyUnion { + interface MyInterface { + MyMethod(int32 a); + }; + }; + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:4: Error: Unexpected 'interface':\n" + r" *interface MyInterface {$"): + parser.Parse(source, "my_file.mojom") + + def testUnionDisallowNestedUnion(self): + """Tests that unions cannot be nested in unions.""" + source = """\ + module my_module; + + union MyUnion { + union MyOtherUnion { + int32 a; + }; + }; + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:4: Error: Unexpected 'union':\n" + r" *union MyOtherUnion {$"): + parser.Parse(source, "my_file.mojom") + + def testUnionDisallowNestedEnum(self): + """Tests that enums cannot be nested in unions.""" + source = """\ + module my_module; + + union MyUnion { + enum MyEnum { + A, + }; + }; + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:4: Error: Unexpected 'enum':\n" + r" *enum MyEnum {$"): + parser.Parse(source, "my_file.mojom") + + def testValidAssociatedKinds(self): + """Tests parsing associated interfaces and requests.""" + source1 = """\ + struct MyStruct { + associated MyInterface a; + associated MyInterface& b; + associated MyInterface? c; + associated MyInterface&? d; + }; + """ + expected1 = ast.Mojom( + None, + ast.ImportList(), + [ast.Struct( + 'MyStruct', + None, + ast.StructBody( + [ast.StructField('a', None, None,'asso<MyInterface>', None), + ast.StructField('b', None, None,'asso<MyInterface&>', None), + ast.StructField('c', None, None,'asso<MyInterface>?', None), + ast.StructField('d', None, None,'asso<MyInterface&>?', + None)]))]) + self.assertEquals(parser.Parse(source1, "my_file.mojom"), expected1) + + source2 = """\ + interface MyInterface { + MyMethod(associated A a) =>(associated B& b); + };""" + expected2 = ast.Mojom( + None, + ast.ImportList(), + [ast.Interface( + 'MyInterface', + None, + ast.InterfaceBody( + ast.Method( + 'MyMethod', + None, + None, + ast.ParameterList( + ast.Parameter('a', None, None, 'asso<A>')), + ast.ParameterList( + ast.Parameter('b', None, None, 'asso<B&>')))))]) + self.assertEquals(parser.Parse(source2, "my_file.mojom"), expected2) + + def testInvalidAssociatedKinds(self): + """Tests that invalid associated interfaces and requests are correctly + detected.""" + source1 = """\ + struct MyStruct { + associated associated SomeInterface a; + }; + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:2: Error: Unexpected 'associated':\n" + r" *associated associated SomeInterface a;$"): + parser.Parse(source1, "my_file.mojom") + + source2 = """\ + struct MyStruct { + associated handle a; + }; + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:2: Error: Unexpected 'handle':\n" + r" *associated handle a;$"): + parser.Parse(source2, "my_file.mojom") + + source3 = """\ + struct MyStruct { + associated? MyInterface& a; + }; + """ + with self.assertRaisesRegexp( + parser.ParseError, + r"^my_file\.mojom:2: Error: Unexpected '\?':\n" + r" *associated\? MyInterface& a;$"): + parser.Parse(source3, "my_file.mojom") + + +if __name__ == "__main__": + unittest.main() diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_parser.py b/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_parser.py new file mode 100755 index 0000000..b160de6 --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_parser.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +# 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. + +"""Simple testing utility to just run the mojom parser.""" + + +import os.path +import sys + +sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), + os.path.pardir, os.path.pardir)) + +from mojom.parse.parser import Parse, ParseError + + +def main(argv): + if len(argv) < 2: + print "usage: %s filename" % argv[0] + return 0 + + for filename in argv[1:]: + with open(filename) as f: + print "%s:" % filename + try: + print Parse(f.read(), filename) + except ParseError, e: + print e + return 1 + + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_translate.py b/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_translate.py new file mode 100755 index 0000000..899d40e --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_translate.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +# 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. + +"""Simple testing utility to just run the mojom translate stage.""" + + +import os.path +import sys + +sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), + os.path.pardir, os.path.pardir)) + +from mojom.parse.parser import Parse +from mojom.parse.translate import Translate + + +def main(argv): + if len(argv) < 2: + print "usage: %s filename" % sys.argv[0] + return 1 + + for filename in argv[1:]: + with open(filename) as f: + print "%s:" % filename + print Translate(Parse(f.read(), filename), + os.path.splitext(os.path.basename(filename))[0]) + + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/parse/translate_unittest.py b/mojo/public/tools/bindings/pylib/mojom_tests/parse/translate_unittest.py new file mode 100644 index 0000000..2520332 --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom_tests/parse/translate_unittest.py @@ -0,0 +1,80 @@ +# 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. + +import imp +import os.path +import sys +import unittest + +def _GetDirAbove(dirname): + """Returns the directory "above" this file containing |dirname| (which must + also be "above" this file).""" + path = os.path.abspath(__file__) + while True: + path, tail = os.path.split(path) + assert tail + if tail == dirname: + return path + +try: + imp.find_module("mojom") +except ImportError: + sys.path.append(os.path.join(_GetDirAbove("pylib"), "pylib")) +from mojom.parse import ast +from mojom.parse import translate + + +class TranslateTest(unittest.TestCase): + """Tests |parser.Parse()|.""" + + def testSimpleArray(self): + """Tests a simple int32[].""" + # pylint: disable=W0212 + self.assertEquals(translate._MapKind("int32[]"), "a:i32") + + def testAssociativeArray(self): + """Tests a simple uint8{string}.""" + # pylint: disable=W0212 + self.assertEquals(translate._MapKind("uint8{string}"), "m[s][u8]") + + def testLeftToRightAssociativeArray(self): + """Makes sure that parsing is done from right to left on the internal kinds + in the presence of an associative array.""" + # pylint: disable=W0212 + self.assertEquals(translate._MapKind("uint8[]{string}"), "m[s][a:u8]") + + def testTranslateSimpleUnions(self): + """Makes sure that a simple union is translated correctly.""" + tree = ast.Mojom( + None, + ast.ImportList(), + [ast.Union("SomeUnion", None, ast.UnionBody( + [ast.UnionField("a", None, None, "int32"), + ast.UnionField("b", None, None, "string")]))]) + expected = [{ + "name": "SomeUnion", + "fields": [{"kind": "i32", "name": "a"}, + {"kind": "s", "name": "b"}]}] + actual = translate.Translate(tree, "mojom_tree") + self.assertEquals(actual["unions"], expected) + + def testMapTreeForTypeRaisesWithDuplicate(self): + """Verifies _MapTreeForType() raises when passed two values with the same + name.""" + methods = [ast.Method('dup', None, None, ast.ParameterList(), None), + ast.Method('dup', None, None, ast.ParameterList(), None)] + self.assertRaises(Exception, translate._MapTreeForType, + (lambda x: x, methods, '', 'scope')) + + def testAssociatedKinds(self): + """Tests type spec translation of associated interfaces and requests.""" + # pylint: disable=W0212 + self.assertEquals(translate._MapKind("asso<SomeInterface>?"), + "?asso:x:SomeInterface") + self.assertEquals(translate._MapKind("asso<SomeInterface&>?"), + "?asso:r:x:SomeInterface") + + +if __name__ == "__main__": + unittest.main() diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/support/__init__.py b/mojo/public/tools/bindings/pylib/mojom_tests/support/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom_tests/support/__init__.py diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/support/find_files.py b/mojo/public/tools/bindings/pylib/mojom_tests/support/find_files.py new file mode 100644 index 0000000..2a4b17b --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom_tests/support/find_files.py @@ -0,0 +1,32 @@ +# 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. + +import fnmatch +from os import walk +from os.path import join +import sys + + +def FindFiles(top, pattern, **kwargs): + """Finds files under |top| matching the glob pattern |pattern|, returning a + list of paths.""" + matches = [] + for dirpath, _, filenames in walk(top, **kwargs): + for filename in fnmatch.filter(filenames, pattern): + matches.append(join(dirpath, filename)) + return matches + + +def main(argv): + if len(argv) != 3: + print "usage: %s path pattern" % argv[0] + return 1 + + for filename in FindFiles(argv[1], argv[2]): + print filename + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/support/run_bindings_generator.py b/mojo/public/tools/bindings/pylib/mojom_tests/support/run_bindings_generator.py new file mode 100644 index 0000000..20ef461 --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom_tests/support/run_bindings_generator.py @@ -0,0 +1,47 @@ +# 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. + +import os.path +from subprocess import check_call +import sys + + +def RunBindingsGenerator(out_dir, root_dir, mojom_file, extra_flags=None): + out_dir = os.path.abspath(out_dir) + root_dir = os.path.abspath(root_dir) + mojom_file = os.path.abspath(mojom_file) + + # The mojom file should be under the root directory somewhere. + assert mojom_file.startswith(root_dir) + mojom_reldir = os.path.dirname(os.path.relpath(mojom_file, root_dir)) + + # TODO(vtl): Abstract out the "main" functions, so that we can just import + # the bindings generator (which would be more portable and easier to use in + # tests). + this_dir = os.path.dirname(os.path.abspath(__file__)) + # We're in src/mojo/public/tools/bindings/pylib/mojom_tests/support; + # mojom_bindings_generator.py is in .../bindings. + bindings_generator = os.path.join(this_dir, os.pardir, os.pardir, os.pardir, + "mojom_bindings_generator.py") + + args = ["python", bindings_generator, + "-o", os.path.join(out_dir, mojom_reldir)] + if extra_flags: + args.extend(extra_flags) + args.append(mojom_file) + + check_call(args) + + +def main(argv): + if len(argv) < 4: + print "usage: %s out_dir root_dir mojom_file [extra_flags]" % argv[0] + return 1 + + RunBindingsGenerator(argv[1], argv[2], argv[3], extra_flags=argv[4:]) + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/mojo/public/tools/gn/zip.py b/mojo/public/tools/gn/zip.py new file mode 100755 index 0000000..12c1879 --- /dev/null +++ b/mojo/public/tools/gn/zip.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +# +# 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. + +"""Archives a set of files. +""" + +import ast +import optparse +import os +import sys +import zipfile + +def DoZip(inputs, link_inputs, zip_inputs, output, base_dir): + files = [] + with zipfile.ZipFile(output, 'w', zipfile.ZIP_DEFLATED) as outfile: + for f in inputs: + file_name = os.path.relpath(f, base_dir) + files.append(file_name) + outfile.write(f, file_name) + for f in link_inputs: + realf = os.path.realpath(f) # Resolve symlinks. + file_name = os.path.relpath(realf, base_dir) + files.append(file_name) + outfile.write(realf, file_name) + for zf_name in zip_inputs: + with zipfile.ZipFile(zf_name, 'r') as zf: + for f in zf.namelist(): + if f not in files: + files.append(f) + with zf.open(f) as zff: + outfile.writestr(f, zff.read()) + + +def main(): + parser = optparse.OptionParser() + + parser.add_option('--inputs', help='List of files to archive.') + parser.add_option('--link-inputs', + help='List of files to archive. Symbolic links are resolved.') + parser.add_option('--zip-inputs', help='List of zip files to re-archive.') + parser.add_option('--output', help='Path to output archive.') + parser.add_option('--base-dir', + help='If provided, the paths in the archive will be ' + 'relative to this directory', default='.') + + options, _ = parser.parse_args() + + inputs = [] + if (options.inputs): + inputs = ast.literal_eval(options.inputs) + link_inputs = [] + if options.link_inputs: + link_inputs = ast.literal_eval(options.link_inputs) + zip_inputs = [] + if options.zip_inputs: + zip_inputs = ast.literal_eval(options.zip_inputs) + output = options.output + base_dir = options.base_dir + + DoZip(inputs, link_inputs, zip_inputs, output, base_dir) + +if __name__ == '__main__': + sys.exit(main()) diff --git a/mojo/public/tools/prepend.py b/mojo/public/tools/prepend.py new file mode 100755 index 0000000..de70a82 --- /dev/null +++ b/mojo/public/tools/prepend.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python +# +# 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. + +""" +Prepends a given file with a given line. This can be used to add a shebang line +to a generated file. +""" + +import optparse +import os +import shutil +import sys + + +def main(): + parser = optparse.OptionParser() + parser.add_option('--input', help='The file to prepend the line to.') + parser.add_option('--line', help='The line to be prepended.') + parser.add_option('--output', help='The output file.') + + options, _ = parser.parse_args() + input_path = options.input + output_path = options.output + line = options.line + + # Warning - this reads all of the input file into memory. + with open(output_path, 'w') as output_file: + output_file.write(line + '\n') + with open(input_path, 'r') as input_file: + shutil.copyfileobj(input_file, output_file) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/mojo/runner/BUILD.gn b/mojo/runner/BUILD.gn index 5334789..159af7d 100644 --- a/mojo/runner/BUILD.gn +++ b/mojo/runner/BUILD.gn @@ -4,8 +4,8 @@ import("//mojo/generate_mojo_shell_assets_list.gni") import("//mojo/public/mojo_application.gni") +import("//mojo/public/tools/bindings/mojom.gni") import("//testing/test.gni") -import("//third_party/mojo/src/mojo/public/tools/bindings/mojom.gni") group("runner") { testonly = true diff --git a/mojo/runner/android/android_handler.cc b/mojo/runner/android/android_handler.cc index 9cc4a81..98313e3 100644 --- a/mojo/runner/android/android_handler.cc +++ b/mojo/runner/android/android_handler.cc @@ -12,10 +12,10 @@ #include "jni/AndroidHandler_jni.h" #include "mojo/application/public/cpp/application_impl.h" #include "mojo/common/data_pipe_utils.h" +#include "mojo/public/c/system/main.h" #include "mojo/runner/android/run_android_application_function.h" #include "mojo/runner/host/native_application_support.h" #include "mojo/util/filename_util.h" -#include "third_party/mojo/src/mojo/public/c/system/main.h" #include "url/gurl.h" using base::android::AttachCurrentThread; diff --git a/mojo/runner/child/BUILD.gn b/mojo/runner/child/BUILD.gn index fe89ecb..edf5f97 100644 --- a/mojo/runner/child/BUILD.gn +++ b/mojo/runner/child/BUILD.gn @@ -3,7 +3,7 @@ # found in the LICENSE file. import("//mojo/public/mojo_application.gni") -import("//third_party/mojo/src/mojo/public/tools/bindings/mojom.gni") +import("//mojo/public/tools/bindings/mojom.gni") group("child") { testonly = true diff --git a/mojo/runner/child/runner_connection.cc b/mojo/runner/child/runner_connection.cc index 015df41..c7758c4 100644 --- a/mojo/runner/child/runner_connection.cc +++ b/mojo/runner/child/runner_connection.cc @@ -13,11 +13,11 @@ #include "base/threading/thread.h" #include "base/threading/thread_checker.h" #include "mojo/message_pump/message_pump_mojo.h" +#include "mojo/public/cpp/bindings/binding.h" #include "mojo/runner/child/child_controller.mojom.h" #include "third_party/mojo/src/mojo/edk/embedder/embedder.h" #include "third_party/mojo/src/mojo/edk/embedder/platform_channel_pair.h" #include "third_party/mojo/src/mojo/edk/embedder/scoped_platform_handle.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h" namespace mojo { namespace runner { diff --git a/mojo/runner/context.cc b/mojo/runner/context.cc index 850cb5a..a21323b 100644 --- a/mojo/runner/context.cc +++ b/mojo/runner/context.cc @@ -28,6 +28,7 @@ #include "mojo/application/public/cpp/application_delegate.h" #include "mojo/application/public/cpp/application_impl.h" #include "mojo/package_manager/package_manager_impl.h" +#include "mojo/public/cpp/bindings/strong_binding.h" #include "mojo/runner/host/in_process_native_runner.h" #include "mojo/runner/host/out_of_process_native_runner.h" #include "mojo/runner/register_local_aliases.h" @@ -43,7 +44,6 @@ #include "mojo/shell/switches.h" #include "mojo/util/filename_util.h" #include "third_party/mojo/src/mojo/edk/embedder/embedder.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h" #include "url/gurl.h" namespace mojo { diff --git a/mojo/runner/host/BUILD.gn b/mojo/runner/host/BUILD.gn index 4227baa..f1890f8 100644 --- a/mojo/runner/host/BUILD.gn +++ b/mojo/runner/host/BUILD.gn @@ -4,8 +4,8 @@ import("//mojo/generate_mojo_shell_assets_list.gni") import("//mojo/public/mojo_application.gni") +import("//mojo/public/tools/bindings/mojom.gni") import("//testing/test.gni") -import("//third_party/mojo/src/mojo/public/tools/bindings/mojom.gni") group("host") { testonly = true diff --git a/mojo/runner/host/child_process.cc b/mojo/runner/host/child_process.cc index fac23ec..8d2ab95 100644 --- a/mojo/runner/host/child_process.cc +++ b/mojo/runner/host/child_process.cc @@ -22,6 +22,8 @@ #include "base/threading/thread.h" #include "base/threading/thread_checker.h" #include "mojo/message_pump/message_pump_mojo.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/system/core.h" #include "mojo/runner/child/child_controller.mojom.h" #include "mojo/runner/host/native_application_support.h" #include "mojo/runner/host/switches.h" @@ -30,8 +32,6 @@ #include "third_party/mojo/src/mojo/edk/embedder/platform_channel_pair.h" #include "third_party/mojo/src/mojo/edk/embedder/process_delegate.h" #include "third_party/mojo/src/mojo/edk/embedder/scoped_platform_handle.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h" -#include "third_party/mojo/src/mojo/public/cpp/system/core.h" #if defined(OS_LINUX) && !defined(OS_ANDROID) #include "base/rand_util.h" diff --git a/mojo/runner/host/child_process_host.cc b/mojo/runner/host/child_process_host.cc index 8cc2b01..6074b5b 100644 --- a/mojo/runner/host/child_process_host.cc +++ b/mojo/runner/host/child_process_host.cc @@ -14,10 +14,10 @@ #include "base/process/launch.h" #include "base/task_runner.h" #include "base/thread_task_runner_handle.h" +#include "mojo/public/cpp/bindings/interface_ptr_info.h" +#include "mojo/public/cpp/system/core.h" #include "mojo/runner/host/switches.h" #include "third_party/mojo/src/mojo/edk/embedder/embedder.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/interface_ptr_info.h" -#include "third_party/mojo/src/mojo/public/cpp/system/core.h" #if defined(OS_LINUX) && !defined(OS_ANDROID) #include "sandbox/linux/services/namespace_sandbox.h" diff --git a/mojo/runner/host/native_application_support.cc b/mojo/runner/host/native_application_support.cc index eae4e98..5b53744 100644 --- a/mojo/runner/host/native_application_support.cc +++ b/mojo/runner/host/native_application_support.cc @@ -9,10 +9,10 @@ #include "base/files/file_util.h" #include "base/logging.h" #include "mojo/platform_handle/platform_handle_private_thunks.h" -#include "third_party/mojo/src/mojo/public/platform/native/gles2_impl_chromium_extension_thunks.h" -#include "third_party/mojo/src/mojo/public/platform/native/gles2_impl_thunks.h" -#include "third_party/mojo/src/mojo/public/platform/native/gles2_thunks.h" -#include "third_party/mojo/src/mojo/public/platform/native/system_thunks.h" +#include "mojo/public/platform/native/gles2_impl_chromium_extension_thunks.h" +#include "mojo/public/platform/native/gles2_impl_thunks.h" +#include "mojo/public/platform/native/gles2_thunks.h" +#include "mojo/public/platform/native/system_thunks.h" namespace mojo { namespace runner { diff --git a/mojo/runner/host/native_application_support.h b/mojo/runner/host/native_application_support.h index 209cc18..dca22de 100644 --- a/mojo/runner/host/native_application_support.h +++ b/mojo/runner/host/native_application_support.h @@ -6,7 +6,7 @@ #define MOJO_RUNNER_HOST_NATIVE_APPLICATION_SUPPORT_H_ #include "base/native_library.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h" +#include "mojo/public/cpp/bindings/interface_request.h" namespace base { class FilePath; diff --git a/mojo/runner/shell_apptest.cc b/mojo/runner/shell_apptest.cc new file mode 100644 index 0000000..f0eb95c --- /dev/null +++ b/mojo/runner/shell_apptest.cc @@ -0,0 +1,199 @@ +// Copyright 2015 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. + +#include "base/base_paths.h" +#include "base/bind.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/path_service.h" +#include "base/run_loop.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "mojo/application/public/cpp/application_impl.h" +#include "mojo/application/public/cpp/application_test_base.h" +#include "mojo/common/data_pipe_utils.h" +#include "mojo/public/cpp/system/macros.h" +#include "mojo/runner/kPingable.h" +#include "mojo/runner/test/pingable.mojom.h" +#include "mojo/services/http_server/public/cpp/http_server_util.h" +#include "mojo/services/http_server/public/interfaces/http_server.mojom.h" +#include "mojo/services/http_server/public/interfaces/http_server_factory.mojom.h" +#include "mojo/services/network/public/interfaces/net_address.mojom.h" + +namespace mojo { +namespace { + +std::string GetURL(uint16_t port, const std::string& path) { + return base::StringPrintf("http://127.0.0.1:%u/%s", + static_cast<unsigned>(port), path.c_str()); +} + +class GetHandler : public http_server::HttpHandler { + public: + GetHandler(InterfaceRequest<http_server::HttpHandler> request, uint16_t port) + : binding_(this, request.Pass()), port_(port) { + } + ~GetHandler() override {} + + private: + // http_server::HttpHandler: + void HandleRequest( + http_server::HttpRequestPtr request, + const Callback<void(http_server::HttpResponsePtr)>& callback) override { + http_server::HttpResponsePtr response; + if (base::StartsWith(request->relative_url, "/app", + base::CompareCase::SENSITIVE)) { + response = http_server::CreateHttpResponse( + 200, std::string(kPingable.data, kPingable.size)); + response->content_type = "application/octet-stream"; + } else if (request->relative_url == "/redirect") { + response = http_server::HttpResponse::New(); + response->status_code = 302; + response->custom_headers.insert("Location", GetURL(port_, "app")); + } else { + NOTREACHED(); + } + + callback.Run(response.Pass()); + } + + Binding<http_server::HttpHandler> binding_; + uint16_t port_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(GetHandler); +}; + +typedef test::ApplicationTestBase ShellAppTest; + +class ShellHTTPAppTest : public test::ApplicationTestBase { + public: + ShellHTTPAppTest() : ApplicationTestBase() {} + ~ShellHTTPAppTest() override {} + + protected: + // ApplicationTestBase: + void SetUp() override { + ApplicationTestBase::SetUp(); + + application_impl()->ConnectToService("mojo:http_server", + &http_server_factory_); + + NetAddressPtr local_address(NetAddress::New()); + local_address->family = NET_ADDRESS_FAMILY_IPV4; + local_address->ipv4 = NetAddressIPv4::New(); + local_address->ipv4->addr.resize(4); + local_address->ipv4->addr[0] = 127; + local_address->ipv4->addr[1] = 0; + local_address->ipv4->addr[2] = 0; + local_address->ipv4->addr[3] = 1; + local_address->ipv4->port = 0; + http_server_factory_->CreateHttpServer(GetProxy(&http_server_), + local_address.Pass()); + + http_server_->GetPort([this](uint16_t p) { port_ = p; }); + EXPECT_TRUE(http_server_.WaitForIncomingResponse()); + + InterfacePtr<http_server::HttpHandler> http_handler; + handler_.reset(new GetHandler(GetProxy(&http_handler).Pass(), port_)); + http_server_->SetHandler(".*", http_handler.Pass(), + [](bool result) { EXPECT_TRUE(result); }); + EXPECT_TRUE(http_server_.WaitForIncomingResponse()); + } + + std::string GetURL(const std::string& path) { + return ::mojo::GetURL(port_, path); + } + + http_server::HttpServerFactoryPtr http_server_factory_; + http_server::HttpServerPtr http_server_; + scoped_ptr<GetHandler> handler_; + uint16_t port_; + + private: + MOJO_DISALLOW_COPY_AND_ASSIGN(ShellHTTPAppTest); +}; + +// Test that we can load apps over http. +TEST_F(ShellHTTPAppTest, Http) { + InterfacePtr<Pingable> pingable; + application_impl()->ConnectToService(GetURL("app"), &pingable); + pingable->Ping("hello", + [this](const String& app_url, const String& connection_url, + const String& message) { + EXPECT_EQ(GetURL("app"), app_url); + EXPECT_EQ(GetURL("app"), connection_url); + EXPECT_EQ("hello", message); + base::MessageLoop::current()->QuitWhenIdle(); + }); + base::RunLoop().Run(); +} + +// Test that redirects work. +// TODO(aa): Test that apps receive the correct URL parameters. +TEST_F(ShellHTTPAppTest, Redirect) { + InterfacePtr<Pingable> pingable; + application_impl()->ConnectToService(GetURL("redirect"), &pingable); + pingable->Ping("hello", + [this](const String& app_url, const String& connection_url, + const String& message) { + EXPECT_EQ(GetURL("app"), app_url); + EXPECT_EQ(GetURL("app"), connection_url); + EXPECT_EQ("hello", message); + base::MessageLoop::current()->QuitWhenIdle(); + }); + base::RunLoop().Run(); +} + +// Test that querystring is not considered when resolving http applications. +// TODO(aa|qsr): Fix this test on Linux ASAN http://crbug.com/463662 +#if defined(ADDRESS_SANITIZER) +#define MAYBE_QueryHandling DISABLED_QueryHandling +#else +#define MAYBE_QueryHandling QueryHandling +#endif // ADDRESS_SANITIZER +TEST_F(ShellHTTPAppTest, MAYBE_QueryHandling) { + InterfacePtr<Pingable> pingable1; + InterfacePtr<Pingable> pingable2; + application_impl()->ConnectToService(GetURL("app?foo"), &pingable1); + application_impl()->ConnectToService(GetURL("app?bar"), &pingable2); + + int num_responses = 0; + auto callback = [this, &num_responses](const String& app_url, + const String& connection_url, + const String& message) { + EXPECT_EQ(GetURL("app"), app_url); + EXPECT_EQ("hello", message); + ++num_responses; + if (num_responses == 1) { + EXPECT_EQ(GetURL("app?foo"), connection_url); + } else if (num_responses == 2) { + EXPECT_EQ(GetURL("app?bar"), connection_url); + base::MessageLoop::current()->QuitWhenIdle(); + } else { + CHECK(false); + } + }; + pingable1->Ping("hello", callback); + pingable2->Ping("hello", callback); + base::RunLoop().Run(); +} + +// mojo: URLs can have querystrings too +TEST_F(ShellAppTest, MojoURLQueryHandling) { + InterfacePtr<Pingable> pingable; + application_impl()->ConnectToService("mojo:pingable_app?foo", &pingable); + auto callback = [this](const String& app_url, const String& connection_url, + const String& message) { + EXPECT_TRUE(base::EndsWith(app_url, "/pingable_app.mojo", + base::CompareCase::SENSITIVE)); + EXPECT_EQ(app_url.To<std::string>() + "?foo", connection_url); + EXPECT_EQ("hello", message); + base::MessageLoop::current()->QuitWhenIdle(); + }; + pingable->Ping("hello", callback); + base::RunLoop().Run(); +} + +} // namespace +} // namespace mojo diff --git a/mojo/runner/shell_test_base.h b/mojo/runner/shell_test_base.h index 861b023..73343a8 100644 --- a/mojo/runner/shell_test_base.h +++ b/mojo/runner/shell_test_base.h @@ -9,10 +9,10 @@ #include "base/macros.h" #include "base/message_loop/message_loop.h" +#include "mojo/public/cpp/bindings/interface_ptr_info.h" +#include "mojo/public/cpp/system/core.h" #include "mojo/runner/context.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/interface_ptr_info.h" -#include "third_party/mojo/src/mojo/public/cpp/system/core.h" class GURL; diff --git a/mojo/runner/shell_test_base_unittest.cc b/mojo/runner/shell_test_base_unittest.cc index 3d2cca1..998d55b 100644 --- a/mojo/runner/shell_test_base_unittest.cc +++ b/mojo/runner/shell_test_base_unittest.cc @@ -9,11 +9,11 @@ #include "base/macros.h" #include "base/message_loop/message_loop.h" #include "base/strings/utf_string_conversions.h" +#include "mojo/public/cpp/bindings/interface_ptr.h" +#include "mojo/public/cpp/system/core.h" #include "mojo/services/test_service/test_request_tracker.mojom.h" #include "mojo/services/test_service/test_service.mojom.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/interface_ptr.h" -#include "third_party/mojo/src/mojo/public/cpp/system/core.h" #include "url/gurl.h" using mojo::test::ServiceReport; diff --git a/mojo/runner/test/BUILD.gn b/mojo/runner/test/BUILD.gn new file mode 100644 index 0000000..bd94a9d --- /dev/null +++ b/mojo/runner/test/BUILD.gn @@ -0,0 +1,28 @@ +# Copyright 2015 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("//mojo/public/mojo_application.gni") +import("//mojo/public/tools/bindings/mojom.gni") + +mojom("bindings") { + sources = [ + "pingable.mojom", + ] +} + +mojo_native_application("pingable_app") { + output_name = "pingable_app" + + testonly = true + + sources = [ + "pingable_app.cc", + ] + + deps = [ + ":bindings", + "//mojo/application/public/cpp", + "//mojo/public/cpp/bindings:callback", + ] +} diff --git a/mojo/runner/test/pingable_app.cc b/mojo/runner/test/pingable_app.cc index f700044..2930a51 100644 --- a/mojo/runner/test/pingable_app.cc +++ b/mojo/runner/test/pingable_app.cc @@ -6,11 +6,11 @@ #include "mojo/application/public/cpp/application_impl.h" #include "mojo/application/public/cpp/application_runner.h" #include "mojo/application/public/cpp/interface_factory.h" +#include "mojo/public/c/system/main.h" +#include "mojo/public/cpp/bindings/callback.h" +#include "mojo/public/cpp/bindings/interface_request.h" +#include "mojo/public/cpp/bindings/strong_binding.h" #include "mojo/runner/test/pingable.mojom.h" -#include "third_party/mojo/src/mojo/public/c/system/main.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/callback.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h" namespace mojo { diff --git a/mojo/services/DEPS b/mojo/services/DEPS index a7ee274..2bbe62d 100644 --- a/mojo/services/DEPS +++ b/mojo/services/DEPS @@ -3,7 +3,6 @@ include_rules = [ "+mojo/common", "+mojo/public", "+jni", - "+third_party/mojo/src/mojo/public", # TODO(abarth) Instead of having the services depend on the shell, we # probably should create a layer below the services. diff --git a/mojo/services/network/BUILD.gn b/mojo/services/network/BUILD.gn index 4dec4880..6b20a9b 100644 --- a/mojo/services/network/BUILD.gn +++ b/mojo/services/network/BUILD.gn @@ -97,6 +97,7 @@ source_set("lib") { "//mojo/common:common_base", "//mojo/common:url_type_converters", "//mojo/message_pump", + "//mojo/public/cpp/system:system", "//mojo/services/network/public/cpp", "//mojo/services/network/public/interfaces", "//net", @@ -104,7 +105,6 @@ source_set("lib") { "//net:http_server", "//url", "//sql/mojo", - "//third_party/mojo/src/mojo/public/cpp/system:system", ] } @@ -119,10 +119,10 @@ source_set("sources") { ":lib", "//base", "//mojo/application/public/cpp", + "//mojo/public/c/system:for_shared_library", + "//mojo/public/cpp/bindings:bindings", "//mojo/services/network/public/cpp", "//mojo/services/network/public/interfaces", - "//third_party/mojo/src/mojo/public/c/system:for_shared_library", - "//third_party/mojo/src/mojo/public/cpp/bindings:bindings", ] } @@ -144,11 +144,11 @@ mojo_native_application("apptests") { "//mojo/application/public/cpp:test_support", "//mojo/common:common_base", "//mojo/message_pump", + "//mojo/public/cpp/bindings:callback", "//mojo/services/network/public/cpp", "//mojo/services/network/public/interfaces", "//net", "//net:test_support", "//testing/gtest", - "//third_party/mojo/src/mojo/public/cpp/bindings:callback", ] } diff --git a/mojo/services/network/cookie_store_impl.h b/mojo/services/network/cookie_store_impl.h index 9448184..adfa85c 100644 --- a/mojo/services/network/cookie_store_impl.h +++ b/mojo/services/network/cookie_store_impl.h @@ -6,8 +6,8 @@ #define MOJO_SERVICES_NETWORK_COOKIE_STORE_IMPL_H_ #include "mojo/application/public/cpp/app_lifetime_helper.h" +#include "mojo/public/cpp/bindings/strong_binding.h" #include "mojo/services/network/public/interfaces/cookie_store.mojom.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h" #include "url/gurl.h" namespace mojo { diff --git a/mojo/services/network/http_connection_impl.cc b/mojo/services/network/http_connection_impl.cc index 965c27e..2d23f5f 100644 --- a/mojo/services/network/http_connection_impl.cc +++ b/mojo/services/network/http_connection_impl.cc @@ -13,6 +13,8 @@ #include "base/stl_util.h" #include "base/strings/string_util.h" #include "mojo/message_pump/handle_watcher.h" +#include "mojo/public/cpp/bindings/type_converter.h" +#include "mojo/public/cpp/system/data_pipe.h" #include "mojo/services/network/http_server_impl.h" #include "mojo/services/network/net_adapters.h" #include "mojo/services/network/public/cpp/web_socket_read_queue.h" @@ -24,8 +26,6 @@ #include "net/server/http_server.h" #include "net/server/http_server_request_info.h" #include "net/server/http_server_response_info.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/type_converter.h" -#include "third_party/mojo/src/mojo/public/cpp/system/data_pipe.h" namespace mojo { diff --git a/mojo/services/network/http_connection_impl.h b/mojo/services/network/http_connection_impl.h index c54218d..71f1fc4 100644 --- a/mojo/services/network/http_connection_impl.h +++ b/mojo/services/network/http_connection_impl.h @@ -10,9 +10,9 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" +#include "mojo/public/cpp/bindings/binding.h" #include "mojo/services/network/public/interfaces/http_connection.mojom.h" #include "mojo/services/network/public/interfaces/http_message.mojom.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h" namespace net { class HttpServerRequestInfo; diff --git a/mojo/services/network/main.cc b/mojo/services/network/main.cc index b8051be..45216b1 100644 --- a/mojo/services/network/main.cc +++ b/mojo/services/network/main.cc @@ -4,8 +4,8 @@ #include "base/message_loop/message_loop.h" #include "mojo/application/public/cpp/application_runner.h" +#include "mojo/public/c/system/main.h" #include "mojo/services/network/network_service_delegate.h" -#include "third_party/mojo/src/mojo/public/c/system/main.h" MojoResult MojoMain(MojoHandle shell_handle) { mojo::ApplicationRunner runner(new mojo::NetworkServiceDelegate); diff --git a/mojo/services/network/net_address_type_converters.h b/mojo/services/network/net_address_type_converters.h index a1af10e..f999908 100644 --- a/mojo/services/network/net_address_type_converters.h +++ b/mojo/services/network/net_address_type_converters.h @@ -5,9 +5,9 @@ #ifndef MOJO_SERVICES_NETWORK_NET_ADDRESS_TYPE_CONVERTERS_H_ #define MOJO_SERVICES_NETWORK_NET_ADDRESS_TYPE_CONVERTERS_H_ +#include "mojo/public/cpp/bindings/type_converter.h" #include "mojo/services/network/public/interfaces/net_address.mojom.h" #include "net/base/ip_endpoint.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/type_converter.h" namespace mojo { diff --git a/mojo/services/network/network_service_impl.h b/mojo/services/network/network_service_impl.h index b349cda..fd6455c 100644 --- a/mojo/services/network/network_service_impl.h +++ b/mojo/services/network/network_service_impl.h @@ -7,8 +7,8 @@ #include "base/compiler_specific.h" #include "mojo/application/public/cpp/app_lifetime_helper.h" +#include "mojo/public/cpp/bindings/strong_binding.h" #include "mojo/services/network/public/interfaces/network_service.mojom.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h" #include "url/gurl.h" namespace mojo { diff --git a/mojo/services/network/public/DEPS b/mojo/services/network/public/DEPS index 3a9fdf7..88036f8 100644 --- a/mojo/services/network/public/DEPS +++ b/mojo/services/network/public/DEPS @@ -5,6 +5,6 @@ include_rules = [ "-services", "-testing", "!mojo/message_pump", + "+mojo/public", "+mojo/services/network/public", - "+third_party/mojo/src/mojo/public", ] diff --git a/mojo/services/network/public/cpp/BUILD.gn b/mojo/services/network/public/cpp/BUILD.gn index 7a07989..cca613d 100644 --- a/mojo/services/network/public/cpp/BUILD.gn +++ b/mojo/services/network/public/cpp/BUILD.gn @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import("//third_party/mojo/src/mojo/public/mojo_sdk.gni") +import("//mojo/public/mojo_sdk.gni") mojo_sdk_source_set("cpp") { restrict_external_deps = false @@ -22,7 +22,7 @@ mojo_sdk_source_set("cpp") { "//mojo/application/public/cpp", "//mojo/environment:chromium", "//mojo/message_pump", - "//third_party/mojo/src/mojo/public/c/system", - "//third_party/mojo/src/mojo/public/cpp/system", + "//mojo/public/c/system", + "//mojo/public/cpp/system", ] } diff --git a/mojo/services/network/public/cpp/udp_socket_wrapper.cc b/mojo/services/network/public/cpp/udp_socket_wrapper.cc index 10a1f13..a9c5c11 100644 --- a/mojo/services/network/public/cpp/udp_socket_wrapper.cc +++ b/mojo/services/network/public/cpp/udp_socket_wrapper.cc @@ -6,7 +6,7 @@ #include <assert.h> -#include "third_party/mojo/src/mojo/public/cpp/environment/logging.h" +#include "mojo/public/cpp/environment/logging.h" namespace mojo { namespace { diff --git a/mojo/services/network/public/cpp/udp_socket_wrapper.h b/mojo/services/network/public/cpp/udp_socket_wrapper.h index c45ed47..a3cd370 100644 --- a/mojo/services/network/public/cpp/udp_socket_wrapper.h +++ b/mojo/services/network/public/cpp/udp_socket_wrapper.h @@ -7,8 +7,8 @@ #include <queue> +#include "mojo/public/cpp/bindings/binding.h" #include "network/public/interfaces/udp_socket.mojom.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h" namespace mojo { diff --git a/mojo/services/network/public/cpp/web_socket_read_queue.h b/mojo/services/network/public/cpp/web_socket_read_queue.h index b270892..d63e774 100644 --- a/mojo/services/network/public/cpp/web_socket_read_queue.h +++ b/mojo/services/network/public/cpp/web_socket_read_queue.h @@ -9,7 +9,7 @@ #include "base/memory/scoped_vector.h" #include "base/memory/weak_ptr.h" #include "mojo/message_pump/handle_watcher.h" -#include "third_party/mojo/src/mojo/public/cpp/system/data_pipe.h" +#include "mojo/public/cpp/system/data_pipe.h" namespace mojo { diff --git a/mojo/services/network/public/cpp/web_socket_write_queue.h b/mojo/services/network/public/cpp/web_socket_write_queue.h index d0ba6b2..b7ca161 100644 --- a/mojo/services/network/public/cpp/web_socket_write_queue.h +++ b/mojo/services/network/public/cpp/web_socket_write_queue.h @@ -9,7 +9,7 @@ #include "base/memory/scoped_vector.h" #include "base/memory/weak_ptr.h" #include "mojo/message_pump/handle_watcher.h" -#include "third_party/mojo/src/mojo/public/cpp/system/data_pipe.h" +#include "mojo/public/cpp/system/data_pipe.h" namespace mojo { diff --git a/mojo/services/network/public/interfaces/BUILD.gn b/mojo/services/network/public/interfaces/BUILD.gn index bc777bc..64ad3dc 100644 --- a/mojo/services/network/public/interfaces/BUILD.gn +++ b/mojo/services/network/public/interfaces/BUILD.gn @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import("//third_party/mojo/src/mojo/public/tools/bindings/mojom.gni") +import("//mojo/public/tools/bindings/mojom.gni") mojom("interfaces") { sources = [ diff --git a/mojo/services/network/tcp_bound_socket_impl.h b/mojo/services/network/tcp_bound_socket_impl.h index 94946ba..17d4a001 100644 --- a/mojo/services/network/tcp_bound_socket_impl.h +++ b/mojo/services/network/tcp_bound_socket_impl.h @@ -7,9 +7,9 @@ #include "base/memory/scoped_ptr.h" #include "mojo/application/public/cpp/app_lifetime_helper.h" +#include "mojo/public/cpp/bindings/strong_binding.h" #include "mojo/services/network/public/interfaces/tcp_bound_socket.mojom.h" #include "net/socket/tcp_socket.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h" namespace mojo { diff --git a/mojo/services/network/tcp_connected_socket_impl.h b/mojo/services/network/tcp_connected_socket_impl.h index b1c072e..15d72f3 100644 --- a/mojo/services/network/tcp_connected_socket_impl.h +++ b/mojo/services/network/tcp_connected_socket_impl.h @@ -9,9 +9,9 @@ #include "base/memory/weak_ptr.h" #include "mojo/application/public/cpp/app_lifetime_helper.h" #include "mojo/message_pump/handle_watcher.h" +#include "mojo/public/cpp/bindings/binding.h" #include "mojo/services/network/public/interfaces/tcp_connected_socket.mojom.h" #include "net/socket/tcp_socket.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h" namespace mojo { diff --git a/mojo/services/network/tcp_server_socket_impl.h b/mojo/services/network/tcp_server_socket_impl.h index 3adb288..84ce309 100644 --- a/mojo/services/network/tcp_server_socket_impl.h +++ b/mojo/services/network/tcp_server_socket_impl.h @@ -7,10 +7,10 @@ #include "base/memory/scoped_ptr.h" #include "mojo/application/public/cpp/app_lifetime_helper.h" +#include "mojo/public/cpp/bindings/strong_binding.h" #include "mojo/services/network/public/interfaces/tcp_server_socket.mojom.h" #include "net/base/ip_endpoint.h" #include "net/socket/tcp_socket.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h" namespace mojo { diff --git a/mojo/services/network/udp_socket_apptest.cc b/mojo/services/network/udp_socket_apptest.cc index 5b2032f..0c92ede 100644 --- a/mojo/services/network/udp_socket_apptest.cc +++ b/mojo/services/network/udp_socket_apptest.cc @@ -8,12 +8,12 @@ #include "mojo/application/public/cpp/application_connection.h" #include "mojo/application/public/cpp/application_impl.h" #include "mojo/application/public/cpp/application_test_base.h" +#include "mojo/public/cpp/bindings/callback.h" #include "mojo/services/network/public/cpp/udp_socket_wrapper.h" #include "mojo/services/network/public/interfaces/network_service.mojom.h" #include "mojo/services/network/public/interfaces/udp_socket.mojom.h" #include "net/base/net_errors.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/callback.h" namespace mojo { namespace service { diff --git a/mojo/services/network/udp_socket_impl.h b/mojo/services/network/udp_socket_impl.h index 3da68b3..8a671ce 100644 --- a/mojo/services/network/udp_socket_impl.h +++ b/mojo/services/network/udp_socket_impl.h @@ -10,10 +10,10 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" #include "mojo/application/public/cpp/app_lifetime_helper.h" +#include "mojo/public/cpp/bindings/strong_binding.h" #include "mojo/services/network/public/interfaces/udp_socket.mojom.h" #include "net/base/ip_endpoint.h" #include "net/udp/udp_socket.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h" namespace net { class IOBuffer; diff --git a/mojo/services/network/url_loader_factory_impl.h b/mojo/services/network/url_loader_factory_impl.h index 1d6c7c6..e2bd446 100644 --- a/mojo/services/network/url_loader_factory_impl.h +++ b/mojo/services/network/url_loader_factory_impl.h @@ -7,8 +7,8 @@ #include "base/compiler_specific.h" #include "mojo/application/public/cpp/app_lifetime_helper.h" +#include "mojo/public/cpp/bindings/strong_binding.h" #include "mojo/services/network/public/interfaces/url_loader_factory.mojom.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h" namespace mojo { class NetworkContext; diff --git a/mojo/services/network/url_loader_impl.h b/mojo/services/network/url_loader_impl.h index 88bf89c..0b7b44d 100644 --- a/mojo/services/network/url_loader_impl.h +++ b/mojo/services/network/url_loader_impl.h @@ -9,10 +9,10 @@ #include "base/memory/weak_ptr.h" #include "mojo/application/public/cpp/app_lifetime_helper.h" #include "mojo/message_pump/handle_watcher.h" +#include "mojo/public/cpp/bindings/binding.h" #include "mojo/services/network/public/interfaces/url_loader.mojom.h" #include "net/base/net_errors.h" #include "net/url_request/url_request.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h" namespace mojo { diff --git a/mojo/services/network/url_loader_impl_apptest.cc b/mojo/services/network/url_loader_impl_apptest.cc index 7bbbba6..0fe1749 100644 --- a/mojo/services/network/url_loader_impl_apptest.cc +++ b/mojo/services/network/url_loader_impl_apptest.cc @@ -9,6 +9,7 @@ #include "base/run_loop.h" #include "mojo/application/public/cpp/application_test_base.h" #include "mojo/message_pump/message_pump_mojo.h" +#include "mojo/public/cpp/bindings/interface_request.h" #include "mojo/services/network/network_context.h" #include "mojo/services/network/url_loader_impl.h" #include "net/base/net_errors.h" @@ -17,7 +18,6 @@ #include "net/url_request/url_request_status.h" #include "net/url_request/url_request_test_util.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h" namespace mojo { diff --git a/mojo/services/network/web_socket_factory_impl.h b/mojo/services/network/web_socket_factory_impl.h index 64fd1be..a6e418f 100644 --- a/mojo/services/network/web_socket_factory_impl.h +++ b/mojo/services/network/web_socket_factory_impl.h @@ -7,8 +7,8 @@ #include "base/compiler_specific.h" #include "mojo/application/public/cpp/app_lifetime_helper.h" +#include "mojo/public/cpp/bindings/strong_binding.h" #include "mojo/services/network/public/interfaces/web_socket_factory.mojom.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h" namespace mojo { class NetworkContext; diff --git a/mojo/services/network/web_socket_impl.h b/mojo/services/network/web_socket_impl.h index 0f26fff9..ce9c282 100644 --- a/mojo/services/network/web_socket_impl.h +++ b/mojo/services/network/web_socket_impl.h @@ -7,8 +7,8 @@ #include "base/memory/scoped_ptr.h" #include "mojo/application/public/cpp/app_lifetime_helper.h" +#include "mojo/public/cpp/bindings/strong_binding.h" #include "mojo/services/network/public/interfaces/web_socket.mojom.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h" namespace net { class WebSocketChannel; diff --git a/mojo/services/test_service/BUILD.gn b/mojo/services/test_service/BUILD.gn index 90bf19a..36647da 100644 --- a/mojo/services/test_service/BUILD.gn +++ b/mojo/services/test_service/BUILD.gn @@ -3,7 +3,7 @@ # found in the LICENSE file. import("//mojo/public/mojo_application.gni") -import("//third_party/mojo/src/mojo/public/tools/bindings/mojom.gni") +import("//mojo/public/tools/bindings/mojom.gni") group("test_service") { # Meta-target, don't link into production code. diff --git a/mojo/services/test_service/test_request_tracker_application.cc b/mojo/services/test_service/test_request_tracker_application.cc index cc6869b..6facdc5 100644 --- a/mojo/services/test_service/test_request_tracker_application.cc +++ b/mojo/services/test_service/test_request_tracker_application.cc @@ -8,8 +8,8 @@ #include "mojo/application/public/cpp/application_connection.h" #include "mojo/application/public/cpp/application_runner.h" +#include "mojo/public/c/system/main.h" #include "mojo/services/test_service/test_time_service_impl.h" -#include "third_party/mojo/src/mojo/public/c/system/main.h" namespace mojo { namespace test { diff --git a/mojo/services/test_service/test_request_tracker_application.h b/mojo/services/test_service/test_request_tracker_application.h index 371ee1f..a009f62 100644 --- a/mojo/services/test_service/test_request_tracker_application.h +++ b/mojo/services/test_service/test_request_tracker_application.h @@ -7,8 +7,8 @@ #include "mojo/application/public/cpp/application_delegate.h" #include "mojo/application/public/cpp/interface_factory_impl.h" +#include "mojo/public/cpp/system/macros.h" #include "mojo/services/test_service/test_request_tracker_impl.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" namespace mojo { class ApplicationImpl; diff --git a/mojo/services/test_service/test_request_tracker_impl.h b/mojo/services/test_service/test_request_tracker_impl.h index 2edb5cc..0ed220a 100644 --- a/mojo/services/test_service/test_request_tracker_impl.h +++ b/mojo/services/test_service/test_request_tracker_impl.h @@ -6,9 +6,9 @@ #define SERVICES_TEST_SERVICE_TEST_REQUEST_TRACKER_IMPL_H_ #include "base/memory/weak_ptr.h" +#include "mojo/public/cpp/bindings/strong_binding.h" +#include "mojo/public/cpp/system/macros.h" #include "mojo/services/test_service/test_request_tracker.mojom.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" namespace mojo { class ApplicationConnection; diff --git a/mojo/services/test_service/test_service_application.cc b/mojo/services/test_service/test_service_application.cc index 493551c4..7fd16fa 100644 --- a/mojo/services/test_service/test_service_application.cc +++ b/mojo/services/test_service/test_service_application.cc @@ -8,9 +8,9 @@ #include "mojo/application/public/cpp/application_connection.h" #include "mojo/application/public/cpp/application_runner.h" +#include "mojo/public/c/system/main.h" #include "mojo/services/test_service/test_service_impl.h" #include "mojo/services/test_service/test_time_service_impl.h" -#include "third_party/mojo/src/mojo/public/c/system/main.h" namespace mojo { namespace test { diff --git a/mojo/services/test_service/test_service_application.h b/mojo/services/test_service/test_service_application.h index 704eb40..4ad5559 100644 --- a/mojo/services/test_service/test_service_application.h +++ b/mojo/services/test_service/test_service_application.h @@ -7,7 +7,7 @@ #include "mojo/application/public/cpp/application_delegate.h" #include "mojo/application/public/cpp/interface_factory.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { class ApplicationConnection; diff --git a/mojo/services/test_service/test_service_impl.h b/mojo/services/test_service/test_service_impl.h index 9c8c86d..c6680a2 100644 --- a/mojo/services/test_service/test_service_impl.h +++ b/mojo/services/test_service/test_service_impl.h @@ -6,9 +6,9 @@ #define SERVICES_TEST_SERVICE_TEST_SERVICE_IMPL_H_ #include "base/memory/scoped_ptr.h" +#include "mojo/public/cpp/bindings/strong_binding.h" +#include "mojo/public/cpp/system/macros.h" #include "mojo/services/test_service/test_service.mojom.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" namespace mojo { class ApplicationImpl; diff --git a/mojo/services/test_service/test_time_service_impl.h b/mojo/services/test_service/test_time_service_impl.h index af92964..242f66c 100644 --- a/mojo/services/test_service/test_time_service_impl.h +++ b/mojo/services/test_service/test_time_service_impl.h @@ -6,10 +6,10 @@ #define SERVICES_TEST_SERVICE_TEST_TIME_SERVICE_IMPL_H_ #include "base/memory/scoped_ptr.h" +#include "mojo/public/cpp/bindings/interface_request.h" +#include "mojo/public/cpp/bindings/strong_binding.h" +#include "mojo/public/cpp/system/macros.h" #include "mojo/services/test_service/test_service.mojom.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" namespace mojo { diff --git a/mojo/services/test_service/tracked_service.h b/mojo/services/test_service/tracked_service.h index bd9cd21..62ec14a 100644 --- a/mojo/services/test_service/tracked_service.h +++ b/mojo/services/test_service/tracked_service.h @@ -5,8 +5,8 @@ #ifndef SERVICES_TEST_SERVICE_TRACKED_SERVICE_H_ #define SERVICES_TEST_SERVICE_TRACKED_SERVICE_H_ +#include "mojo/public/cpp/system/macros.h" #include "mojo/services/test_service/test_request_tracker.mojom.h" -#include "third_party/mojo/src/mojo/public/cpp/system/macros.h" namespace mojo { namespace test { diff --git a/mojo/services/tracing/BUILD.gn b/mojo/services/tracing/BUILD.gn index 63d3a9c..4c8421e 100644 --- a/mojo/services/tracing/BUILD.gn +++ b/mojo/services/tracing/BUILD.gn @@ -14,7 +14,7 @@ mojo_native_application("tracing") { deps = [ ":lib", "//mojo/application/public/cpp", - "//third_party/mojo/src/mojo/public/cpp/system", + "//mojo/public/cpp/system", ] } diff --git a/mojo/services/tracing/main.cc b/mojo/services/tracing/main.cc index a7875aa..1810d1b 100644 --- a/mojo/services/tracing/main.cc +++ b/mojo/services/tracing/main.cc @@ -4,8 +4,8 @@ #include "mojo/application/public/cpp/application_impl.h" #include "mojo/application/public/cpp/application_runner.h" +#include "mojo/public/c/system/main.h" #include "mojo/services/tracing/tracing_app.h" -#include "third_party/mojo/src/mojo/public/c/system/main.h" MojoResult MojoMain(MojoHandle shell_handle) { mojo::ApplicationRunner runner(new tracing::TracingApp); diff --git a/mojo/services/tracing/public/cpp/BUILD.gn b/mojo/services/tracing/public/cpp/BUILD.gn index bf40151..02eed11 100644 --- a/mojo/services/tracing/public/cpp/BUILD.gn +++ b/mojo/services/tracing/public/cpp/BUILD.gn @@ -15,7 +15,7 @@ source_set("cpp") { deps = [ "//base", "//mojo/application/public/cpp", + "//mojo/public/cpp/bindings", "//mojo/services/tracing/public/interfaces", - "//third_party/mojo/src/mojo/public/cpp/bindings", ] } diff --git a/mojo/services/tracing/public/cpp/trace_provider_impl.h b/mojo/services/tracing/public/cpp/trace_provider_impl.h index dca965f..5774911 100644 --- a/mojo/services/tracing/public/cpp/trace_provider_impl.h +++ b/mojo/services/tracing/public/cpp/trace_provider_impl.h @@ -8,9 +8,9 @@ #include "base/macros.h" #include "base/memory/ref_counted_memory.h" #include "base/memory/weak_ptr.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/interface_request.h" #include "mojo/services/tracing/public/interfaces/tracing.mojom.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h" namespace mojo { diff --git a/mojo/services/tracing/public/interfaces/BUILD.gn b/mojo/services/tracing/public/interfaces/BUILD.gn index 5b4ecae..8323805 100644 --- a/mojo/services/tracing/public/interfaces/BUILD.gn +++ b/mojo/services/tracing/public/interfaces/BUILD.gn @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import("//third_party/mojo/src/mojo/public/tools/bindings/mojom.gni") +import("//mojo/public/tools/bindings/mojom.gni") mojom("interfaces") { sources = [ diff --git a/mojo/services/tracing/trace_data_sink.h b/mojo/services/tracing/trace_data_sink.h index 658a265..f945d4f 100644 --- a/mojo/services/tracing/trace_data_sink.h +++ b/mojo/services/tracing/trace_data_sink.h @@ -8,7 +8,7 @@ #include <string> #include "base/basictypes.h" -#include "third_party/mojo/src/mojo/public/cpp/system/data_pipe.h" +#include "mojo/public/cpp/system/data_pipe.h" namespace tracing { diff --git a/mojo/services/tracing/trace_recorder_impl.h b/mojo/services/tracing/trace_recorder_impl.h index ea74dd6..55640e1 100644 --- a/mojo/services/tracing/trace_recorder_impl.h +++ b/mojo/services/tracing/trace_recorder_impl.h @@ -6,10 +6,10 @@ #define MOJO_SERVICES_TRACING_TRACE_RECORDER_IMPL_H_ #include "base/macros.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/string.h" #include "mojo/services/tracing/public/interfaces/tracing.mojom.h" #include "mojo/services/tracing/trace_data_sink.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/string.h" namespace tracing { diff --git a/mojo/services/tracing/tracing_app.h b/mojo/services/tracing/tracing_app.h index 2c70fdd..ccf7a35 100644 --- a/mojo/services/tracing/tracing_app.h +++ b/mojo/services/tracing/tracing_app.h @@ -12,10 +12,10 @@ #include "mojo/application/public/cpp/interface_factory.h" #include "mojo/common/weak_binding_set.h" #include "mojo/common/weak_interface_ptr_set.h" +#include "mojo/public/cpp/bindings/strong_binding.h" #include "mojo/services/tracing/public/interfaces/tracing.mojom.h" #include "mojo/services/tracing/trace_data_sink.h" #include "mojo/services/tracing/trace_recorder_impl.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h" namespace tracing { diff --git a/mojo/services/updater/BUILD.gn b/mojo/services/updater/BUILD.gn index 664b819..6e3bd81 100644 --- a/mojo/services/updater/BUILD.gn +++ b/mojo/services/updater/BUILD.gn @@ -2,8 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import("//mojo/public/mojo.gni") -import("$mojo_sdk_root/mojo/public/tools/bindings/mojom.gni") +import("//mojo/public/tools/bindings/mojom.gni") mojom("updater") { sources = [ diff --git a/mojo/shell/BUILD.gn b/mojo/shell/BUILD.gn index e7bd7ab..b54c7b5 100644 --- a/mojo/shell/BUILD.gn +++ b/mojo/shell/BUILD.gn @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import("//third_party/mojo/src/mojo/public/tools/bindings/mojom.gni") +import("//mojo/public/tools/bindings/mojom.gni") import("//testing/test.gni") source_set("shell") { @@ -43,7 +43,7 @@ source_set("shell") { "//base", "//mojo/application/public/interfaces", "//mojo/common", - "//third_party/mojo/src/mojo/public/cpp/bindings", + "//mojo/public/cpp/bindings", "//mojo/services/network/public/interfaces", "//mojo/services/updater", "//url", @@ -98,9 +98,9 @@ test("mojo_shell_unittests") { "//mojo/application/public/cpp", "//mojo/fetcher", "//mojo/package_manager", + "//mojo/public/cpp/system", "//mojo/util:filename_util", "//third_party/mojo/src/mojo/edk/test:run_all_unittests", - "//third_party/mojo/src/mojo/public/cpp/system", "//testing/gtest", "//url", ] diff --git a/mojo/shell/application_instance.h b/mojo/shell/application_instance.h index ea6a91b..a99a385 100644 --- a/mojo/shell/application_instance.h +++ b/mojo/shell/application_instance.h @@ -11,10 +11,10 @@ #include "base/memory/scoped_ptr.h" #include "mojo/application/public/interfaces/application.mojom.h" #include "mojo/application/public/interfaces/shell.mojom.h" +#include "mojo/public/cpp/bindings/binding.h" #include "mojo/shell/capability_filter.h" #include "mojo/shell/connect_to_application_params.h" #include "mojo/shell/identity.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h" #include "url/gurl.h" namespace mojo { diff --git a/mojo/shell/application_loader.h b/mojo/shell/application_loader.h index a13b254..f464ab1 100644 --- a/mojo/shell/application_loader.h +++ b/mojo/shell/application_loader.h @@ -7,8 +7,8 @@ #include "base/callback.h" #include "mojo/application/public/interfaces/shell.mojom.h" +#include "mojo/public/cpp/system/core.h" #include "mojo/services/network/public/interfaces/url_loader.mojom.h" -#include "third_party/mojo/src/mojo/public/cpp/system/core.h" #include "url/gurl.h" namespace mojo { diff --git a/mojo/shell/application_manager.cc b/mojo/shell/application_manager.cc index 688cdf4..16c483d 100644 --- a/mojo/shell/application_manager.cc +++ b/mojo/shell/application_manager.cc @@ -11,13 +11,13 @@ #include "base/stl_util.h" #include "base/strings/string_util.h" #include "base/trace_event/trace_event.h" +#include "mojo/public/cpp/bindings/binding.h" #include "mojo/shell/application_instance.h" #include "mojo/shell/fetcher.h" #include "mojo/shell/package_manager.h" #include "mojo/shell/query_util.h" #include "mojo/shell/shell_application_loader.h" #include "mojo/shell/switches.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h" namespace mojo { namespace shell { diff --git a/mojo/shell/application_manager.h b/mojo/shell/application_manager.h index 3b8d90a..16afd44 100644 --- a/mojo/shell/application_manager.h +++ b/mojo/shell/application_manager.h @@ -14,14 +14,14 @@ #include "mojo/application/public/interfaces/application.mojom.h" #include "mojo/application/public/interfaces/service_provider.mojom.h" #include "mojo/application/public/interfaces/shell.mojom.h" +#include "mojo/public/cpp/bindings/interface_ptr_info.h" +#include "mojo/public/cpp/bindings/interface_request.h" #include "mojo/shell/application_loader.h" #include "mojo/shell/capability_filter.h" #include "mojo/shell/connect_to_application_params.h" #include "mojo/shell/fetcher.h" #include "mojo/shell/identity.h" #include "mojo/shell/native_runner.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/interface_ptr_info.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h" #include "url/gurl.h" namespace base { diff --git a/mojo/shell/application_manager_unittest.cc b/mojo/shell/application_manager_unittest.cc index 72a4182..e13f616f 100644 --- a/mojo/shell/application_manager_unittest.cc +++ b/mojo/shell/application_manager_unittest.cc @@ -13,6 +13,7 @@ #include "mojo/application/public/cpp/application_impl.h" #include "mojo/application/public/cpp/interface_factory.h" #include "mojo/application/public/interfaces/service_provider.mojom.h" +#include "mojo/public/cpp/bindings/strong_binding.h" #include "mojo/shell/application_loader.h" #include "mojo/shell/application_manager.h" #include "mojo/shell/connect_util.h" @@ -21,7 +22,6 @@ #include "mojo/shell/test.mojom.h" #include "mojo/shell/test_package_manager.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h" namespace mojo { namespace shell { diff --git a/mojo/shell/capability_filter.h b/mojo/shell/capability_filter.h index 685496f..a2a9dfd 100644 --- a/mojo/shell/capability_filter.h +++ b/mojo/shell/capability_filter.h @@ -8,7 +8,7 @@ #include <map> #include <set> -#include "third_party/mojo/src/mojo/public/cpp/bindings/array.h" +#include "mojo/public/cpp/bindings/array.h" namespace mojo { namespace shell { diff --git a/mojo/shell/capability_filter_test.cc b/mojo/shell/capability_filter_test.cc index 6214554..b50f717 100644 --- a/mojo/shell/capability_filter_test.cc +++ b/mojo/shell/capability_filter_test.cc @@ -12,9 +12,9 @@ #include "mojo/application/public/cpp/interface_factory.h" #include "mojo/application/public/cpp/service_provider_impl.h" #include "mojo/common/weak_binding_set.h" +#include "mojo/public/cpp/bindings/strong_binding.h" #include "mojo/shell/application_loader.h" #include "mojo/shell/package_manager.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h" namespace mojo { namespace shell { diff --git a/mojo/shell/connect_to_application_params.h b/mojo/shell/connect_to_application_params.h index 457db98..badca06 100644 --- a/mojo/shell/connect_to_application_params.h +++ b/mojo/shell/connect_to_application_params.h @@ -10,9 +10,9 @@ #include "base/callback.h" #include "mojo/application/public/interfaces/service_provider.mojom.h" #include "mojo/application/public/interfaces/shell.mojom.h" +#include "mojo/public/cpp/bindings/interface_request.h" #include "mojo/services/network/public/interfaces/url_loader.mojom.h" #include "mojo/shell/identity.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h" #include "url/gurl.h" namespace mojo { diff --git a/mojo/shell/connect_util.h b/mojo/shell/connect_util.h index 8d1a8d6..8c36b3d 100644 --- a/mojo/shell/connect_util.h +++ b/mojo/shell/connect_util.h @@ -5,8 +5,8 @@ #ifndef MOJO_SHELL_CONNECT_UTIL_H_ #define MOJO_SHELL_CONNECT_UTIL_H_ -#include "third_party/mojo/src/mojo/public/cpp/bindings/interface_ptr.h" -#include "third_party/mojo/src/mojo/public/cpp/system/handle.h" +#include "mojo/public/cpp/bindings/interface_ptr.h" +#include "mojo/public/cpp/system/handle.h" class GURL; diff --git a/mojo/shell/data_pipe_peek.h b/mojo/shell/data_pipe_peek.h index a26e079..0bcf1dd 100644 --- a/mojo/shell/data_pipe_peek.h +++ b/mojo/shell/data_pipe_peek.h @@ -7,7 +7,7 @@ #include <string> -#include "third_party/mojo/src/mojo/public/cpp/system/core.h" +#include "mojo/public/cpp/system/core.h" namespace mojo { namespace shell { diff --git a/mojo/shell/native_runner.h b/mojo/shell/native_runner.h index d4d5b8b..77e73c8 100644 --- a/mojo/shell/native_runner.h +++ b/mojo/shell/native_runner.h @@ -8,7 +8,7 @@ #include "base/callback_forward.h" #include "base/memory/scoped_ptr.h" #include "mojo/application/public/interfaces/application.mojom.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h" +#include "mojo/public/cpp/bindings/interface_request.h" #if defined(OS_WIN) #undef DELETE diff --git a/mojo/shell/static_application_loader.cc b/mojo/shell/static_application_loader.cc index a0f90a8..482021d 100644 --- a/mojo/shell/static_application_loader.cc +++ b/mojo/shell/static_application_loader.cc @@ -13,7 +13,7 @@ #include "mojo/application/public/cpp/application_delegate.h" #include "mojo/application/public/cpp/application_runner.h" #include "mojo/application/public/interfaces/application.mojom.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h" +#include "mojo/public/cpp/bindings/interface_request.h" namespace mojo { namespace shell { diff --git a/mojo/util/capture_util.h b/mojo/util/capture_util.h index c4ccd0d..4b89eb7 100644 --- a/mojo/util/capture_util.h +++ b/mojo/util/capture_util.h @@ -5,7 +5,7 @@ #ifndef MOJO_UTIL_CAPTURE_UTIL_H_ #define MOJO_UTIL_CAPTURE_UTIL_H_ -#include "third_party/mojo/src/mojo/public/cpp/bindings/lib/template_util.h" +#include "mojo/public/cpp/bindings/lib/template_util.h" namespace mojo { |