diff --git a/.gitmodules b/.gitmodules index bab7f071..71b84a4c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,3 +22,6 @@ [submodule "third_party/zlib"] path = third_party/zlib url = https://chromium.googlesource.com/chromium/src/third_party/zlib.git +[submodule "third_party/icu"] + path = third_party/icu + url = https://chromium.googlesource.com/chromium/deps/icu.git diff --git a/.gn b/.gn index b21600ab..e5b2befc 100644 --- a/.gn +++ b/.gn @@ -28,7 +28,6 @@ default_args = { # https://cs.chromium.org/chromium/src/docs/ccache_mac.md clang_use_chrome_plugins = false - v8_enable_i18n_support = false v8_monolithic = false v8_use_external_startup_data = false v8_use_snapshot = true diff --git a/src/icu.rs b/src/icu.rs new file mode 100644 index 00000000..64ef0fa8 --- /dev/null +++ b/src/icu.rs @@ -0,0 +1,46 @@ +extern "C" { + fn udata_setCommonData_68(this: *const u8, error_code: *mut i32); +} + +/// This function bypasses the normal ICU data loading process and allows you to force ICU's system +/// data to come out of a user-specified area in memory. +/// +/// ICU data must be at least 8-aligned, and should be 16-aligned. See +/// https://unicode-org.github.io/icu/userguide/icudata +/// +/// The format of this data is that of the icu common data file, as is generated by the pkgdata +/// tool with mode=common or mode=dll. You can read in a whole common mode file and pass the +/// address to the start of the data, or (with the appropriate link options) pass in the pointer to +/// the data that has been loaded from a dll by the operating system, as shown in this code: +/// +/// extern const char U_IMPORT U_ICUDATA_ENTRY_POINT []; +/// // U_ICUDATA_ENTRY_POINT is same as entry point specified to pkgdata tool +/// UErrorCode status = U_ZERO_ERROR; +/// +/// udata_setCommonData(&U_ICUDATA_ENTRY_POINT, &status); +/// +/// It is important that the declaration be as above. The entry point must not be declared as an +/// extern void*. +/// +/// Starting with ICU 4.4, it is possible to set several data packages, one per call to this +/// function. udata_open() will look for data in the multiple data packages in the order in which +/// they were set. The position of the linked-in or default-name ICU .data package in the search +/// list depends on when the first data item is loaded that is not contained in the already +/// explicitly set packages. If data was loaded implicitly before the first call to this function +/// (for example, via opening a converter, constructing a UnicodeString from default-codepage data, +/// using formatting or collation APIs, etc.), then the default data will be first in the list. +/// +/// This function has no effect on application (non ICU) data. See udata_setAppData() for similar +/// functionality for application data. +// TODO(ry) Map error code to something useful. +pub fn set_common_data(data: &'static [u8]) -> Result<(), i32> { + let mut error_code = 0i32; + unsafe { + udata_setCommonData_68(data.as_ptr(), &mut error_code); + } + if error_code == 0 { + Ok(()) + } else { + Err(error_code) + } +} diff --git a/src/lib.rs b/src/lib.rs index 51bca49e..7cf2efe6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,6 +45,7 @@ mod external_references; mod fixed_array; mod function; mod handle; +pub mod icu; mod isolate; mod isolate_create_params; mod module; diff --git a/tests/test_api.rs b/tests/test_api.rs index 5c41c568..78e1f155 100644 --- a/tests/test_api.rs +++ b/tests/test_api.rs @@ -4715,3 +4715,59 @@ fn prepare_stack_trace_callback() { v8::Integer::new(scope, 42).into() } } + +const ICU_DATA: &[u8; 10413584] = + include_bytes!("../third_party/icu/common/icudtl.dat"); + +#[test] +fn icu_date() { + assert!(v8::icu::set_common_data(ICU_DATA).is_ok()); + + let _setup_guard = setup(); + let isolate = &mut v8::Isolate::new(Default::default()); + { + let scope = &mut v8::HandleScope::new(isolate); + let context = v8::Context::new(scope); + let scope = &mut v8::ContextScope::new(scope, context); + let source = r#" + (new Date(Date.UTC(2020, 5, 26, 7, 0, 0))).toLocaleString("de-DE", { + weekday: "long", + year: "numeric", + month: "long", + day: "numeric", + }); + "#; + let value = eval(scope, source).unwrap(); + let date_de_val = v8::String::new(scope, "Freitag, 26. Juni 2020").unwrap(); + assert!(value.is_string()); + assert!(value.strict_equals(date_de_val.into())); + } +} + +#[test] +fn icu_set_common_data_fail() { + const BAD_DATA: &[u8; 3] = &[1, 2, 3]; + assert!(v8::icu::set_common_data(BAD_DATA).is_err()); +} + +#[test] +fn icu_format() { + assert!(v8::icu::set_common_data(ICU_DATA).is_ok()); + + let _setup_guard = setup(); + let isolate = &mut v8::Isolate::new(Default::default()); + { + let scope = &mut v8::HandleScope::new(isolate); + let context = v8::Context::new(scope); + let scope = &mut v8::ContextScope::new(scope, context); + let source = r#" + new Intl.NumberFormat("ja-JP", { style: "currency", currency: "JPY" }).format( + 1230000, + ); + "#; + let value = eval(scope, source).unwrap(); + let currency_jpy_val = v8::String::new(scope, "¥1,230,000").unwrap(); + assert!(value.is_string()); + assert!(value.strict_equals(currency_jpy_val.into())); + } +} diff --git a/third_party/icu b/third_party/icu new file mode 160000 index 00000000..f4147b22 --- /dev/null +++ b/third_party/icu @@ -0,0 +1 @@ +Subproject commit f4147b227f174e03f273053bb49fde33426d05bc