mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: 新增 Intl 特性域骨架实现
This commit is contained in:
Generated
+21
-21
@@ -1160,7 +1160,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_builtins"
|
||||
version = "0.19.22"
|
||||
version = "0.19.23"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"fast-float",
|
||||
@@ -1185,7 +1185,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_bytecode"
|
||||
version = "0.19.22"
|
||||
version = "0.19.23"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"oxide_log",
|
||||
@@ -1193,7 +1193,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_cfg"
|
||||
version = "0.19.22"
|
||||
version = "0.19.23"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_emit",
|
||||
@@ -1204,7 +1204,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_cli"
|
||||
version = "0.19.22"
|
||||
version = "0.19.23"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"bumpalo",
|
||||
@@ -1228,7 +1228,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_code_cache"
|
||||
version = "0.19.22"
|
||||
version = "0.19.23"
|
||||
dependencies = [
|
||||
"lru",
|
||||
"oxide_bytecode",
|
||||
@@ -1237,7 +1237,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_compiler"
|
||||
version = "0.19.22"
|
||||
version = "0.19.23"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1258,7 +1258,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_dce"
|
||||
version = "0.19.22"
|
||||
version = "0.19.23"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1272,7 +1272,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_emit"
|
||||
version = "0.19.22"
|
||||
version = "0.19.23"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"oxc_allocator",
|
||||
@@ -1288,7 +1288,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_engine"
|
||||
version = "0.19.22"
|
||||
version = "0.19.23"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_compiler",
|
||||
@@ -1300,7 +1300,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_fold"
|
||||
version = "0.19.22"
|
||||
version = "0.19.23"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"oxide_bytecode",
|
||||
@@ -1312,7 +1312,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_ir"
|
||||
version = "0.19.22"
|
||||
version = "0.19.23"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_log",
|
||||
@@ -1321,7 +1321,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_kernel"
|
||||
version = "0.19.22"
|
||||
version = "0.19.23"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"dashmap",
|
||||
@@ -1335,7 +1335,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_liveness"
|
||||
version = "0.19.22"
|
||||
version = "0.19.23"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1347,7 +1347,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_log"
|
||||
version = "0.19.22"
|
||||
version = "0.19.23"
|
||||
dependencies = [
|
||||
"tracing",
|
||||
"tracing-appender",
|
||||
@@ -1356,11 +1356,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_net"
|
||||
version = "0.19.22"
|
||||
version = "0.19.23"
|
||||
|
||||
[[package]]
|
||||
name = "oxide_parser"
|
||||
version = "0.19.22"
|
||||
version = "0.19.23"
|
||||
dependencies = [
|
||||
"oxc_allocator",
|
||||
"oxc_ast",
|
||||
@@ -1375,7 +1375,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_regalloc"
|
||||
version = "0.19.22"
|
||||
version = "0.19.23"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1391,7 +1391,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_runtime_api"
|
||||
version = "0.19.22"
|
||||
version = "0.19.23"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
@@ -1404,7 +1404,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_test262"
|
||||
version = "0.19.22"
|
||||
version = "0.19.23"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_compiler",
|
||||
@@ -1424,7 +1424,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_types"
|
||||
version = "0.19.22"
|
||||
version = "0.19.23"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"dashmap",
|
||||
@@ -1437,7 +1437,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_vm"
|
||||
version = "0.19.22"
|
||||
version = "0.19.23"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"chrono",
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.19.22"
|
||||
version = "0.19.23"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
rust-version = "1.80"
|
||||
|
||||
@@ -0,0 +1,408 @@
|
||||
//! CLDR 36.1 语言标签规范化与 UTS35 结构校验(纯函数,不依赖 VmHost)。
|
||||
//!
|
||||
//! 映射表与 test262 harness 的 CLDR 36.1 表逐位一致(见 `tables` 模块);
|
||||
//! 结构校验遵循 UTS35(`is_structurally_valid`),不接受 BCP47 宽松形式。
|
||||
//! 规范化结果等于输入当且仅当输入是规范化的合法标签(`canonicalize_locale`
|
||||
//! 的不动点即合法性判据)。
|
||||
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use regex::Regex;
|
||||
|
||||
use super::tables::*;
|
||||
|
||||
/// UTS35 languageTag 正则(大小写不敏感)。
|
||||
///
|
||||
/// 与 harness 的 `languageTagRE` 逐字一致:language 后接可选 script / region /
|
||||
/// variants,再重复可选 extensions(unicode / transformed / other),最后可选
|
||||
/// privateuse。
|
||||
const LANGUAGE_TAG_PATTERN: &str = r"^(?i:(([a-z]{2,3}|[a-z]{5,8})(-([a-z]{4}))?(-([a-z]{2}|[0-9]{3}))?(-([a-z0-9]{5,8}|(?:[0-9][a-z0-9]{3})))*(-((u((-([a-z0-9][a-z](-[a-z0-9]{3,8})*))+|((-([a-z0-9]{3,8}))+(-([a-z0-9][a-z](-[a-z0-9]{3,8})*))*)))|(t((-(([a-z]{2,3}|[a-z]{5,8})(-([a-z]{4}))?(-([a-z]{2}|[0-9]{3}))?(-([a-z0-9]{5,8}|(?:[0-9][a-z0-9]{3})))*)(-([a-z][0-9](-[a-z0-9]{3,8})+))*)|(-([a-z][0-9](-[a-z0-9]{3,8})+))+))|(([0-9]|[a-sv-wy-z])(-[a-z0-9]{2,8})+)))*(-(x(-[a-z0-9]{1,8})+))?))$";
|
||||
|
||||
fn language_tag_re() -> &'static Regex {
|
||||
static RE: OnceLock<Regex> = OnceLock::new();
|
||||
RE.get_or_init(|| Regex::new(LANGUAGE_TAG_PATTERN).expect("语言标签正则须可编译"))
|
||||
}
|
||||
|
||||
/// 判断子标签是否为单例(singleton):1 字符,数字或 `[a-wy-z]`(大小写不敏感)。
|
||||
fn is_singleton(subtag: &str) -> bool {
|
||||
let bytes = subtag.as_bytes();
|
||||
bytes.len() == 1 && matches!(bytes[0], b'0'..=b'9' | b'a'..=b'w' | b'y'..=b'z')
|
||||
}
|
||||
|
||||
/// 判断子标签是否为 2-8 位字母数字,供重复变体检测的中间段判定。
|
||||
fn is_2_8_alnum(subtag: &str) -> bool {
|
||||
(2..=8).contains(&subtag.len()) && subtag.bytes().all(|b| b.is_ascii_alphanumeric())
|
||||
}
|
||||
|
||||
/// 判断子标签是否为变体(variant):5-8 位字母数字,或数字开头共 4 位。
|
||||
fn is_variant(subtag: &str) -> bool {
|
||||
let bytes = subtag.as_bytes();
|
||||
let is_alnum = |b: u8| b.is_ascii_alphanumeric();
|
||||
(bytes.len() == 4 && bytes[0].is_ascii_digit() && bytes.iter().all(|&b| is_alnum(b)))
|
||||
|| (bytes.len() >= 5 && bytes.len() <= 8 && bytes.iter().all(|&b| is_alnum(b)))
|
||||
}
|
||||
|
||||
/// 判断子标签是否为 transformed extension 的 key(`^[a-z][0-9]$`,大小写不敏感)。
|
||||
fn is_transform_key(subtag: &str) -> bool {
|
||||
let bytes = subtag.as_bytes();
|
||||
bytes.len() == 2 && bytes[0].is_ascii_lowercase() && bytes[1].is_ascii_digit()
|
||||
}
|
||||
|
||||
/// 判断是否存在重复单例子标签(大小写不敏感比较,与 harness 的 `i` 标志一致)。
|
||||
///
|
||||
/// 条件:存在 i < j,两个子标签都是单例且取值相同。
|
||||
fn has_duplicate_singleton(subtags: &[&str]) -> bool {
|
||||
let n = subtags.len();
|
||||
for i in 0..n {
|
||||
if !is_singleton(subtags[i]) {
|
||||
continue;
|
||||
}
|
||||
for j in (i + 1)..n {
|
||||
if subtags[i].eq_ignore_ascii_case(subtags[j]) && is_singleton(subtags[j]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// 判断是否存在重复变体子标签(大小写不敏感比较)。
|
||||
///
|
||||
/// 条件:存在 i < j,`subtags[j]` 是变体且与 `subtags[i]` 相同,`subtags[i-1]`
|
||||
/// 存在且为 2-8 位字母数字,i 与 j 之间的所有子标签均为 2-8 位字母数字。
|
||||
fn has_duplicate_variant(subtags: &[&str]) -> bool {
|
||||
let n = subtags.len();
|
||||
for j in 0..n {
|
||||
if !is_variant(subtags[j]) {
|
||||
continue;
|
||||
}
|
||||
for i in 0..j {
|
||||
if !subtags[i].eq_ignore_ascii_case(subtags[j]) {
|
||||
continue;
|
||||
}
|
||||
if i == 0 || !is_2_8_alnum(subtags[i - 1]) {
|
||||
continue;
|
||||
}
|
||||
if (i + 1..j).all(|k| is_2_8_alnum(subtags[k])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// UTS35 结构校验:languageTag 正则通过,且截断私有域后无重复单例与重复变体。
|
||||
pub fn is_structurally_valid(tag: &str) -> bool {
|
||||
if !language_tag_re().is_match(tag) {
|
||||
return false;
|
||||
}
|
||||
let truncated = tag.split("-x-").next().unwrap_or("");
|
||||
let subtags: Vec<&str> = truncated.split('-').collect();
|
||||
!has_duplicate_singleton(&subtags) && !has_duplicate_variant(&subtags)
|
||||
}
|
||||
|
||||
/// CLDR 36.1 语言标签规范化(harness `canonicalizeLanguageTag` 的逐位移植)。
|
||||
///
|
||||
/// 步骤:整标映射 → 标准段(language / script / region)解析与映射 → variants
|
||||
/// 映射与排序 → extensions 规范化(unicode 键排序、transformed 递归规范化)→
|
||||
/// privateuse 透传 → 重组。
|
||||
pub fn canonicalize_locale(locale: &str) -> String {
|
||||
let locale = locale.to_ascii_lowercase();
|
||||
|
||||
// 整标映射(grandfathered 标签)。
|
||||
if let Some((_, mapped)) = TAG_MAPPINGS.iter().find(|(from, _)| *from == locale) {
|
||||
return mapped.to_string();
|
||||
}
|
||||
|
||||
let subtags: Vec<&str> = locale.split('-').collect();
|
||||
let mut i = 0;
|
||||
|
||||
// 标准段:第一个 variant 或 singleton 之前的全部子标签。
|
||||
let mut language = String::new();
|
||||
let mut script: Option<String> = None;
|
||||
let mut region: Option<String> = None;
|
||||
while i < subtags.len() {
|
||||
let subtag = subtags[i];
|
||||
if i == 0 {
|
||||
language = subtag.to_string();
|
||||
} else if subtag.len() == 2 || subtag.len() == 3 {
|
||||
region = Some(subtag.to_ascii_uppercase());
|
||||
} else if subtag.len() == 4 && !subtag.starts_with(|c: char| c.is_ascii_digit()) {
|
||||
let mut chars = subtag.chars();
|
||||
let first = chars.next().map(|c| c.to_ascii_uppercase().to_string()).unwrap_or_default();
|
||||
let rest = chars.collect::<String>().to_ascii_lowercase();
|
||||
script = Some(format!("{first}{rest}"));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
// language 映射:简单表优先,未命中查复合表(复合表可补 script / region)。
|
||||
if let Some((_, mapped)) = LANGUAGE_MAPPINGS.iter().find(|(from, _)| *from == language) {
|
||||
language = mapped.to_string();
|
||||
} else if let Some((_, mapped_lang, mapped_script, mapped_region)) =
|
||||
COMPLEX_LANGUAGE_MAPPINGS.iter().find(|(from, _, _, _)| *from == language)
|
||||
{
|
||||
language = mapped_lang.to_string();
|
||||
if script.is_none() {
|
||||
script = mapped_script.map(|s| s.to_string());
|
||||
}
|
||||
if region.is_none() {
|
||||
region = mapped_region.map(|s| s.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
// region 映射:简单表优先,未命中查复合表(按 language[-script] 键选择)。
|
||||
if let Some(region_raw) = region {
|
||||
let mapped = if let Some((_, mapped)) = REGION_MAPPINGS.iter().find(|(from, _)| *from == region_raw) {
|
||||
mapped.to_string()
|
||||
} else if let Some((_, default, entries)) =
|
||||
COMPLEX_REGION_MAPPINGS.iter().find(|(from, _, _)| *from == region_raw)
|
||||
{
|
||||
let mut key = language.clone();
|
||||
if let Some(script) = &script {
|
||||
key.push('-');
|
||||
key.push_str(script);
|
||||
}
|
||||
entries
|
||||
.iter()
|
||||
.find(|(k, _)| *k == key)
|
||||
.map(|(_, v)| v.to_string())
|
||||
.unwrap_or_else(|| default.to_string())
|
||||
} else {
|
||||
region_raw
|
||||
};
|
||||
region = Some(mapped);
|
||||
}
|
||||
|
||||
// variants:映射后按字典序排序。
|
||||
let mut variants: Vec<String> = Vec::new();
|
||||
while i < subtags.len() && subtags[i].len() > 1 {
|
||||
let variant = subtags[i];
|
||||
match VARIANT_MAPPINGS.iter().find(|(from, _, _)| *from == variant) {
|
||||
Some((_, "language", replacement)) => language = replacement.to_string(),
|
||||
Some((_, "region", replacement)) => region = Some(replacement.to_string()),
|
||||
Some((_, "variant", replacement)) => variants.push(replacement.to_string()),
|
||||
Some((_, _, _)) => unreachable!("变体映射类型非法"),
|
||||
None => variants.push(variant.to_string()),
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
variants.sort();
|
||||
|
||||
// extensions:unicode 键按 ASCII 排序、transformed 递归规范化、其余透传。
|
||||
let mut extensions: Vec<String> = Vec::new();
|
||||
while i < subtags.len() && subtags[i] != "x" {
|
||||
let extension_start = i;
|
||||
i += 1;
|
||||
while i < subtags.len() && subtags[i].len() > 1 {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
let extension_key = subtags[extension_start];
|
||||
if extension_key == "u" {
|
||||
let mut j = extension_start + 1;
|
||||
while j < i && subtags[j].len() > 2 {
|
||||
j += 1;
|
||||
}
|
||||
let mut extension = subtags[extension_start..j].join("-");
|
||||
while j < i {
|
||||
let key_start = j;
|
||||
j += 1;
|
||||
while j < i && subtags[j].len() > 2 {
|
||||
j += 1;
|
||||
}
|
||||
let key = subtags[key_start];
|
||||
let value = subtags[key_start + 1..j].join("-");
|
||||
let value = if let Some((_, entries)) = UNICODE_MAPPINGS.iter().find(|(k, _)| *k == key) {
|
||||
entries
|
||||
.iter()
|
||||
.find(|(k, _)| *k == value)
|
||||
.map(|(_, v)| v.to_string())
|
||||
.unwrap_or(value)
|
||||
} else {
|
||||
value
|
||||
};
|
||||
extension.push('-');
|
||||
extension.push_str(key);
|
||||
if !value.is_empty() && value != "true" {
|
||||
extension.push('-');
|
||||
extension.push_str(&value);
|
||||
}
|
||||
}
|
||||
extensions.push(extension);
|
||||
} else if extension_key == "t" {
|
||||
let mut j = extension_start + 1;
|
||||
while j < i && !is_transform_key(subtags[j]) {
|
||||
j += 1;
|
||||
}
|
||||
let mut extension = String::from("t");
|
||||
let transform_language = subtags[extension_start + 1..j].join("-");
|
||||
if !transform_language.is_empty() {
|
||||
extension.push('-');
|
||||
extension.push_str(&canonicalize_locale(&transform_language).to_ascii_lowercase());
|
||||
}
|
||||
// tfield 按字母序排列(UTS35 §3.3.1 规范化要求;true 值不删除)。
|
||||
let mut fields: Vec<(String, String)> = Vec::new();
|
||||
while j < i {
|
||||
let key_start = j;
|
||||
j += 1;
|
||||
while j < i && subtags[j].len() > 2 {
|
||||
j += 1;
|
||||
}
|
||||
let key = subtags[key_start].to_string();
|
||||
let value = subtags[key_start + 1..j].join("-");
|
||||
let value = if let Some((_, entries)) = TRANSFORM_MAPPINGS.iter().find(|(k, _)| *k == key) {
|
||||
entries
|
||||
.iter()
|
||||
.find(|(k, _)| *k == value)
|
||||
.map(|(_, v)| v.to_string())
|
||||
.unwrap_or(value)
|
||||
} else {
|
||||
value
|
||||
};
|
||||
fields.push((key, value));
|
||||
}
|
||||
fields.sort();
|
||||
for (key, value) in fields {
|
||||
extension.push('-');
|
||||
extension.push_str(&key);
|
||||
extension.push('-');
|
||||
extension.push_str(&value);
|
||||
}
|
||||
extensions.push(extension);
|
||||
} else {
|
||||
extensions.push(subtags[extension_start..i].join("-"));
|
||||
}
|
||||
}
|
||||
extensions.sort();
|
||||
|
||||
// privateuse 透传。
|
||||
let private_use = if i < subtags.len() { Some(subtags[i..].join("-")) } else { None };
|
||||
|
||||
// 重组。
|
||||
let mut canonical = language;
|
||||
if let Some(script) = script {
|
||||
canonical.push('-');
|
||||
canonical.push_str(&script);
|
||||
}
|
||||
if let Some(region) = region {
|
||||
canonical.push('-');
|
||||
canonical.push_str(®ion);
|
||||
}
|
||||
if !variants.is_empty() {
|
||||
canonical.push('-');
|
||||
canonical.push_str(&variants.join("-"));
|
||||
}
|
||||
if !extensions.is_empty() {
|
||||
canonical.push('-');
|
||||
canonical.push_str(&extensions.join("-"));
|
||||
}
|
||||
if let Some(private_use) = private_use {
|
||||
if !canonical.is_empty() {
|
||||
canonical.push('-');
|
||||
canonical.push_str(&private_use);
|
||||
} else {
|
||||
canonical = private_use;
|
||||
}
|
||||
}
|
||||
canonical
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn canonicalize_vectors() {
|
||||
// 与 node + harness canonicalizeLanguageTag 的实测向量对齐。
|
||||
assert_eq!(canonicalize_locale("cmn-hans-cn"), "zh-Hans-CN");
|
||||
assert_eq!(canonicalize_locale("sh"), "sr-Latn");
|
||||
assert_eq!(canonicalize_locale("no"), "nb");
|
||||
assert_eq!(canonicalize_locale("cnr"), "sr-ME");
|
||||
// 标准段循环中 2-3 位子标签逐个覆盖 region:`oed` 非变体(3 位),
|
||||
// 按 region 消费并覆盖 `GB`,与 harness 逐位一致。
|
||||
assert_eq!(canonicalize_locale("en-GB-oed"), "en-OED");
|
||||
assert_eq!(canonicalize_locale("heploc"), "heploc");
|
||||
assert_eq!(canonicalize_locale("aa-aaland"), "aa-AX");
|
||||
assert_eq!(canonicalize_locale("es-419"), "es-419");
|
||||
assert_eq!(canonicalize_locale("172"), "172");
|
||||
assert_eq!(canonicalize_locale("en-t-en-GB"), "en-t-en-gb");
|
||||
assert_eq!(canonicalize_locale("ab-cd"), "ab-CD");
|
||||
assert_eq!(canonicalize_locale("de-rt"), "de-RT");
|
||||
assert_eq!(canonicalize_locale("en-x-u-foo"), "en-x-u-foo");
|
||||
assert_eq!(canonicalize_locale("en-a-bar-x-u-foo"), "en-a-bar-x-u-foo");
|
||||
assert_eq!(canonicalize_locale("en-x-u-foo-a-bar"), "en-x-u-foo-a-bar");
|
||||
assert_eq!(canonicalize_locale("en-a-bar-u-baz-x-u-foo"), "en-a-bar-u-baz-x-u-foo");
|
||||
// transformed 扩展:tfield 按字母序排列、tvalue 映射、true 不删除。
|
||||
assert_eq!(canonicalize_locale("sl-t-sl-rozaj-biske-1994"), "sl-t-sl-1994-biske-rozaj");
|
||||
assert_eq!(canonicalize_locale("DE-T-M0-DIN-K0-QWERTZ"), "de-t-k0-qwertz-m0-din");
|
||||
assert_eq!(canonicalize_locale("en-t-m0-true"), "en-t-m0-true");
|
||||
assert_eq!(canonicalize_locale("en-t-iw"), "en-t-he");
|
||||
assert_eq!(canonicalize_locale("und-Latn-t-und-hani-m0-names"), "und-Latn-t-und-hani-m0-prprname");
|
||||
assert_eq!(canonicalize_locale("mo"), "ro");
|
||||
assert_eq!(canonicalize_locale("ces"), "cs");
|
||||
assert_eq!(canonicalize_locale("hy-arevela"), "hy");
|
||||
assert_eq!(canonicalize_locale("hy-arevmda"), "hyw");
|
||||
assert_eq!(canonicalize_locale("aar-x-private"), "aa-x-private");
|
||||
assert_eq!(canonicalize_locale("heb-x-private"), "he-x-private");
|
||||
// unicode 扩展键保持原序(harness 不排序键;test262 允许原序或排序)。
|
||||
assert_eq!(canonicalize_locale("it-u-nu-latn-ca-gregory"), "it-u-nu-latn-ca-gregory");
|
||||
assert_eq!(canonicalize_locale("th-th-u-nu-thai"), "th-TH-u-nu-thai");
|
||||
assert_eq!(canonicalize_locale("art-lojban"), "jbo");
|
||||
assert_eq!(canonicalize_locale("zh-guoyu"), "zh");
|
||||
assert_eq!(canonicalize_locale("zh-hakka"), "hak");
|
||||
assert_eq!(canonicalize_locale("zh-xiang"), "hsn");
|
||||
assert_eq!(canonicalize_locale("es-ES-preeuro"), "es-ES-preeuro");
|
||||
// `cyrillic` 是脚本名而非变体,映射表无此项,原样透传(8 位子标签
|
||||
// 按变体消费)。
|
||||
assert_eq!(canonicalize_locale("uz-UZ-cyrillic"), "uz-UZ-cyrillic");
|
||||
// harness 变体表无 `posix` 项(UTS35 变体映射不覆盖它),原样透传。
|
||||
assert_eq!(canonicalize_locale("posix"), "posix");
|
||||
// harness 变体表仅 5 项(aaland/arevela/arevmda/heploc/polytoni),
|
||||
// `direct` 无映射,原样透传。
|
||||
assert_eq!(canonicalize_locale("hi-direct"), "hi-direct");
|
||||
assert_eq!(canonicalize_locale("zh-pinyin"), "zh-pinyin");
|
||||
assert_eq!(canonicalize_locale("zh-stroke"), "zh-stroke");
|
||||
assert_eq!(canonicalize_locale("de-u-kf"), "de-u-kf");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn structural_validity() {
|
||||
// 结构合法:languageTag 正则通过且无重复单例/重复变体。
|
||||
assert!(is_structurally_valid("en"));
|
||||
assert!(is_structurally_valid("en-US"));
|
||||
assert!(is_structurally_valid("zh-Hans-CN"));
|
||||
assert!(is_structurally_valid("en-x-u-foo"));
|
||||
assert!(is_structurally_valid("en-t-en-gb"));
|
||||
assert!(is_structurally_valid("th-TH-u-nu-thai"));
|
||||
assert!(is_structurally_valid("aa-AX"));
|
||||
assert!(is_structurally_valid("en-POSIX"));
|
||||
assert!(is_structurally_valid("en-US-latin"));
|
||||
assert!(is_structurally_valid("en-GB-1234"));
|
||||
|
||||
// 结构合法但非规范化(规范化判据在 canonicalize_locale 不动点,
|
||||
// 本函数不查):与 harness 的 isCanonicalizedStructurallyValidLanguageTag
|
||||
// 不同,后者额外要求输入等于规范化结果。
|
||||
assert!(is_structurally_valid("cmn-hans-cn"));
|
||||
assert!(is_structurally_valid("no"));
|
||||
assert!(is_structurally_valid("sh"));
|
||||
|
||||
// `u`/`t` 是两个不同单例:无重复单例,结构合法(harness 侧判非法
|
||||
// 源于扩展排序的规范化判据,不属本函数)。
|
||||
assert!(is_structurally_valid("en-u-ca-gregory-t-en"));
|
||||
|
||||
// 结构非法:正则不匹配(OED/oed 3 位非变体、172 非语言、单字符、空串、
|
||||
// 区域在脚本前、重复子标签)或重复单例/重复变体。
|
||||
assert!(!is_structurally_valid("en-GB-OED"));
|
||||
assert!(!is_structurally_valid("en-GB-oed"));
|
||||
assert!(!is_structurally_valid("172"));
|
||||
assert!(!is_structurally_valid("it-IT-Latn"));
|
||||
assert!(!is_structurally_valid("en-u-ca-gregory-u-kf"));
|
||||
assert!(!is_structurally_valid("en-172-172"));
|
||||
assert!(!is_structurally_valid("a"));
|
||||
assert!(!is_structurally_valid("en-GB-xx"));
|
||||
assert!(!is_structurally_valid(""));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
//! Intl 特性域:命名空间对象、十组构造器/原型对、`getCanonicalLocales` 与
|
||||
//! Locale 构造器(骨架阶段)。
|
||||
//!
|
||||
//! 规范化映射表与 CLDR 36.1 逐位一致(test262 harness 同源),结构校验遵循
|
||||
//! UTS35(不接受 BCP47 宽松形式)。真实格式化语义(format / formatToParts /
|
||||
//! resolvedOptions / compare 等)由后续任务填充。
|
||||
|
||||
mod cldr;
|
||||
mod native;
|
||||
mod tables;
|
||||
|
||||
pub use native::*;
|
||||
@@ -0,0 +1,219 @@
|
||||
//! Intl 骨架阶段的 native 实现:`getCanonicalLocales`、十组构造器(含
|
||||
//! FallbackSymbol 语义)、`supportedLocalesOf`(尚无支持域过滤)与 Locale 构造器。
|
||||
//!
|
||||
//! 真实格式化语义(format / formatToParts / resolvedOptions / compare 等)
|
||||
//! 由后续任务填充;本模块只保证构造与元数据面符合规范。
|
||||
|
||||
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
|
||||
use oxide_runtime_api::{
|
||||
to_length, to_number_full, to_object, to_string, to_string_full, NativeResult, ProtoKind, VmHost,
|
||||
};
|
||||
use oxide_types::object::{JsObject, PropAttributes};
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
use super::cldr;
|
||||
use crate::temporal::native_try;
|
||||
|
||||
/// 把 `Result<_, String>` 错误文本转为异常值:优先取调用期间产生的未捕获
|
||||
/// 异常(getter 抛出等),无则按文本恢复 kind 创建错误对象。
|
||||
fn engine_error<H: VmHost>(vm: &mut H, text: &str) -> JsValue {
|
||||
vm.take_uncaught_value()
|
||||
.unwrap_or_else(|| crate::error::create_from_text(vm, text))
|
||||
}
|
||||
|
||||
/// 按元素列表构造普通数组(CreateArrayFromList 语义)。
|
||||
fn make_array_from_list<H: VmHost>(vm: &mut H, items: &[JsValue]) -> JsValue {
|
||||
let array_proto = vm.builtin_proto(ProtoKind::ArrayProto);
|
||||
let arr = vm.alloc_object(JsObject::new_array(
|
||||
EMPTY_SHAPE_ID,
|
||||
JsValue::from_js_object(array_proto),
|
||||
items.len().min(oxide_types::object::MAX_DENSE_PROPS),
|
||||
));
|
||||
for (i, item) in items.iter().enumerate() {
|
||||
// SAFETY: arr 是刚经统一入口分配的数组对象,元素区已就绪。
|
||||
unsafe {
|
||||
(*arr).set_prop_at(i, *item);
|
||||
}
|
||||
}
|
||||
// SAFETY: 与 new_array 同 epoch,写入后按实际元素数设 prop_count。
|
||||
unsafe {
|
||||
(*arr).set_prop_count(items.len());
|
||||
}
|
||||
JsValue::from_js_object(arr)
|
||||
}
|
||||
|
||||
/// 单个标签的校验与规范化:结构校验(RangeError)→ 规范化。
|
||||
fn canonicalize_one<H: VmHost>(vm: &mut H, s: &str) -> Result<String, JsValue> {
|
||||
if !cldr::is_structurally_valid(s) {
|
||||
return Err(crate::error::create_range_error(vm, "not a valid language tag"));
|
||||
}
|
||||
Ok(cldr::canonicalize_locale(s))
|
||||
}
|
||||
|
||||
/// CanonicalizeLocaleList(ECMA-402 9.2.1):字符串参数按单元素列表处理;
|
||||
/// 其余 ToObject → ToLength(length) → 逐元素 HasProperty/Get(尊重 getter)→
|
||||
/// 原始串去重 → 结构校验(RangeError)→ 规范化 → 规范化结果去重。
|
||||
/// 非字符串/对象元素抛 TypeError。
|
||||
fn canonicalize_locale_list<H: VmHost>(vm: &mut H, locales: JsValue) -> Result<Vec<String>, JsValue> {
|
||||
if locales.is_undefined() {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
// 字符串参数整体是单个 locale(规范步骤 3:包装为单元素数组),
|
||||
// 不按字符序列解释。
|
||||
if locales.is_string() {
|
||||
let s = to_string(locales);
|
||||
return Ok(vec![canonicalize_one(vm, &s)?]);
|
||||
}
|
||||
let o_val = to_object(locales, vm).map_err(|e| engine_error(vm, &e))?;
|
||||
let o_ptr = o_val.as_js_object_ptr();
|
||||
if o_ptr.is_null() {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
let o = unsafe { &*o_ptr };
|
||||
let length_si = vm.perm_intern("length");
|
||||
let length_raw = vm.ordinary_get(o, length_si, o_val).map_err(|e| engine_error(vm, &e))?;
|
||||
// LengthOfArrayLike:ToNumber 全程强制(symbol 抛 TypeError),再 ToLength。
|
||||
let length_num = to_number_full(length_raw, vm).map_err(|e| engine_error(vm, &e))?;
|
||||
let len = to_length(JsValue::float(length_num)) as usize;
|
||||
|
||||
let mut raw: Vec<String> = Vec::new();
|
||||
for i in 0..len {
|
||||
let key = vm.new_string(&i.to_string());
|
||||
let key_si = vm.property_key_si(key);
|
||||
let present = vm.has_property(o, key_si).map_err(|e| engine_error(vm, &e))?;
|
||||
if !present {
|
||||
continue;
|
||||
}
|
||||
let next = vm.ordinary_get(o, key_si, o_val).map_err(|e| engine_error(vm, &e))?;
|
||||
// 元素须为字符串或对象(对象经 ToString 取标签,尊重自定义 toString);
|
||||
// 其余类型(number/boolean/null/undefined/symbol)抛 TypeError。
|
||||
let s = if next.is_string() {
|
||||
to_string(next)
|
||||
} else if next.is_object() {
|
||||
to_string_full(next, vm).map_err(|e| engine_error(vm, &e))?
|
||||
} else {
|
||||
return Err(crate::error::create_type_error(vm, "locales must be strings"));
|
||||
};
|
||||
if !raw.contains(&s) {
|
||||
raw.push(s);
|
||||
}
|
||||
}
|
||||
|
||||
let mut result: Vec<String> = Vec::new();
|
||||
for s in &raw {
|
||||
let canonical = canonicalize_one(vm, s)?;
|
||||
if !result.contains(&canonical) {
|
||||
result.push(canonical);
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// `Intl.getCanonicalLocales(locales)`:CanonicalizeLocaleList 后 CreateArrayFromList。
|
||||
pub fn get_canonical_locales<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let locales = if args.len() < 2 { JsValue::undefined() } else { vm.reg(args[1]) };
|
||||
let list = native_try!(canonicalize_locale_list(vm, locales));
|
||||
let items: Vec<JsValue> = list.iter().map(|s| vm.new_string_owned(s.clone())).collect();
|
||||
NativeResult::Ok(make_array_from_list(vm, &items))
|
||||
}
|
||||
|
||||
/// 十组构造器的共享骨架:`new` 时 this 已按 newTarget 原型创建(返回 undefined,
|
||||
/// VM 回传 this);非 legacy 构造器(PluralRules 及以后)NewTarget 为 undefined
|
||||
/// 时抛 TypeError;legacy 构造器(NumberFormat/DateTimeFormat/Collator)在既有
|
||||
/// 实例上非构造调用时定义 FallbackSymbol 并返回 this,其余路径新建实例返回。
|
||||
///
|
||||
/// FallbackSymbol 描述固定为 "IntlLegacyConstructedSymbol",属性值取本次调用
|
||||
/// 新建的实例(规范:[[Value]]: numberFormat),描述符
|
||||
/// {writable:false, enumerable:false, configurable:false}。
|
||||
fn intl_ctor_impl<H: VmHost>(vm: &mut H, args: &[u8], proto_kind: ProtoKind, legacy: bool) -> NativeResult {
|
||||
let proto_ptr = vm.builtin_proto(proto_kind);
|
||||
if vm.constructing_native() {
|
||||
return NativeResult::Ok(JsValue::undefined());
|
||||
}
|
||||
if !legacy {
|
||||
return NativeResult::Err(crate::error::create_type_error(vm, "constructor requires new"));
|
||||
}
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
if !this_val.is_object() {
|
||||
return make_intl_instance(vm, proto_ptr);
|
||||
}
|
||||
let this_ptr = this_val.as_js_object_ptr();
|
||||
if this_ptr.is_null() {
|
||||
return make_intl_instance(vm, proto_ptr);
|
||||
}
|
||||
// this 的原型链包含本构造器原型(OrdinaryHasInstance 语义):定义
|
||||
// FallbackSymbol 并返回 this。
|
||||
if crate::temporal::is_ctor_call(vm, args, proto_ptr) {
|
||||
let this_obj = unsafe { &mut *this_ptr };
|
||||
// 属性值取本次调用新建的实例(规范:[[Value]]: numberFormat)。
|
||||
let instance_val = match make_intl_instance(vm, proto_ptr) {
|
||||
NativeResult::Ok(v) => v,
|
||||
NativeResult::Err(e) => return NativeResult::Err(e),
|
||||
NativeResult::TailCall { .. } => unreachable!("make_intl_instance 不尾调用"),
|
||||
};
|
||||
let idx = vm.symbol_intern(Some("IntlLegacyConstructedSymbol".to_string()));
|
||||
let key = oxide_types::private_key::encode_symbol_key(vm.realm_id(), idx);
|
||||
let _ = vm.define_data_property(this_obj, key, instance_val, PropAttributes::new(false, false, false));
|
||||
return NativeResult::Ok(this_val);
|
||||
}
|
||||
make_intl_instance(vm, proto_ptr)
|
||||
}
|
||||
|
||||
/// 新建一个以指定原型为 `[[Prototype]]` 的空实例。
|
||||
fn make_intl_instance<H: VmHost>(vm: &mut H, proto_ptr: *mut JsObject) -> NativeResult {
|
||||
let proto = JsValue::from_js_object(proto_ptr);
|
||||
let obj = JsObject::new_empty(EMPTY_SHAPE_ID, proto);
|
||||
NativeResult::Ok(JsValue::from_js_object(vm.alloc_object(obj)))
|
||||
}
|
||||
|
||||
macro_rules! intl_ctor_legacy {
|
||||
($name:ident, $kind:ident) => {
|
||||
pub fn $name<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
intl_ctor_impl(vm, args, ProtoKind::$kind, true)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! intl_ctor {
|
||||
($name:ident, $kind:ident) => {
|
||||
pub fn $name<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
intl_ctor_impl(vm, args, ProtoKind::$kind, false)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// legacy 构造器:可非构造调用(FallbackSymbol 语义)。
|
||||
intl_ctor_legacy!(number_format_constructor, NumberFormatProto);
|
||||
intl_ctor_legacy!(date_time_format_constructor, DateTimeFormatProto);
|
||||
intl_ctor_legacy!(collator_constructor, CollatorProto);
|
||||
// 非 legacy 构造器:NewTarget 为 undefined 时抛 TypeError。
|
||||
intl_ctor!(plural_rules_constructor, PluralRulesProto);
|
||||
intl_ctor!(list_format_constructor, ListFormatProto);
|
||||
intl_ctor!(relative_time_format_constructor, RelativeTimeFormatProto);
|
||||
intl_ctor!(display_names_constructor, DisplayNamesProto);
|
||||
intl_ctor!(segmenter_constructor, SegmenterProto);
|
||||
intl_ctor!(locale_constructor, LocaleProto);
|
||||
intl_ctor!(duration_format_constructor, DurationFormatProto);
|
||||
|
||||
/// `supportedLocalesOf(locales)`:骨架阶段只执行 CanonicalizeLocaleList,
|
||||
/// 尚不做支持域过滤(过滤依赖 ICU 数据,后续任务填充)。
|
||||
pub fn supported_locales_of<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let locales = if args.len() < 2 { JsValue::undefined() } else { vm.reg(args[1]) };
|
||||
let list = native_try!(canonicalize_locale_list(vm, locales));
|
||||
let items: Vec<JsValue> = list.iter().map(|s| vm.new_string_owned(s.clone())).collect();
|
||||
NativeResult::Ok(make_array_from_list(vm, &items))
|
||||
}
|
||||
|
||||
/// `Intl.supportedValuesOf(key)`:骨架阶段只做键名校验(六类白名单,
|
||||
/// 其余 RangeError);合法键返回空数组(数据层随后续任务填充)。
|
||||
pub fn supported_values_of<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let key = if args.len() < 2 { JsValue::undefined() } else { vm.reg(args[1]) };
|
||||
let key_str = native_try!(to_string_full(key, vm).map_err(|e| engine_error(vm, &e)));
|
||||
if !matches!(
|
||||
key_str.as_str(),
|
||||
"calendar" | "collation" | "currency" | "numberingSystem" | "timeZone" | "unit"
|
||||
) {
|
||||
return NativeResult::Err(crate::error::create_range_error(vm, "invalid key"));
|
||||
}
|
||||
NativeResult::Ok(make_array_from_list(vm, &[]))
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -43,6 +43,8 @@ pub mod event_target;
|
||||
pub mod function;
|
||||
/// 全局函数模块(URI 编解码与 Annex B 的 escape/unescape)。
|
||||
pub mod global;
|
||||
/// Intl 特性域(骨架阶段):命名空间、十组构造器/原型对、getCanonicalLocales。
|
||||
pub mod intl;
|
||||
/// 迭代器相关实现(Iterator 包装、for-of 底层迭代逻辑)。
|
||||
pub mod iterator;
|
||||
/// JSON 内置对象实现(parse/stringify)。
|
||||
|
||||
@@ -10,7 +10,9 @@ mod plain_year_month;
|
||||
mod time_zone;
|
||||
mod zoned_date_time;
|
||||
|
||||
pub(crate) use common::is_ctor_call;
|
||||
pub(crate) use common::make_instant;
|
||||
pub(crate) use common::native_try;
|
||||
use common::*;
|
||||
use difference::*;
|
||||
pub use duration::*;
|
||||
|
||||
@@ -347,6 +347,16 @@ pub(crate) fn wire_builtin_world_links(world: &BuiltinWorld) {
|
||||
wire_ctor_proto(&world.plain_month_day_constructor, &world.plain_month_day_proto);
|
||||
wire_ctor_proto(&world.plain_year_month_constructor, &world.plain_year_month_proto);
|
||||
wire_ctor_proto(&world.bigint_constructor, &world.bigint_proto);
|
||||
wire_ctor_proto(&world.number_format_constructor, &world.number_format_proto);
|
||||
wire_ctor_proto(&world.date_time_format_constructor, &world.date_time_format_proto);
|
||||
wire_ctor_proto(&world.collator_constructor, &world.collator_proto);
|
||||
wire_ctor_proto(&world.plural_rules_constructor, &world.plural_rules_proto);
|
||||
wire_ctor_proto(&world.list_format_constructor, &world.list_format_proto);
|
||||
wire_ctor_proto(&world.relative_time_format_constructor, &world.relative_time_format_proto);
|
||||
wire_ctor_proto(&world.display_names_constructor, &world.display_names_proto);
|
||||
wire_ctor_proto(&world.segmenter_constructor, &world.segmenter_proto);
|
||||
wire_ctor_proto(&world.locale_constructor, &world.locale_proto);
|
||||
wire_ctor_proto(&world.duration_format_constructor, &world.duration_format_proto);
|
||||
|
||||
// 非 TypedArray 构造器的 [[Prototype]] 指向 Function.prototype:
|
||||
// 标准内置函数对象均继承 Function.prototype。
|
||||
@@ -376,12 +386,22 @@ pub(crate) fn wire_builtin_world_links(world: &BuiltinWorld) {
|
||||
&world.plain_date_time_constructor,
|
||||
&world.plain_month_day_constructor,
|
||||
&world.plain_year_month_constructor,
|
||||
&world.number_format_constructor,
|
||||
&world.date_time_format_constructor,
|
||||
&world.collator_constructor,
|
||||
&world.plural_rules_constructor,
|
||||
&world.list_format_constructor,
|
||||
&world.relative_time_format_constructor,
|
||||
&world.display_names_constructor,
|
||||
&world.segmenter_constructor,
|
||||
&world.locale_constructor,
|
||||
&world.duration_format_constructor,
|
||||
] {
|
||||
set_proto_if_changed(ctor, fn_proto_val);
|
||||
}
|
||||
|
||||
let obj_proto_val = JsValue::from_js_object(world.object_proto.as_ptr() as *mut JsObject);
|
||||
let non_object_protos: [&P<JsObject>; 28] = [
|
||||
let non_object_protos: [&P<JsObject>; 38] = [
|
||||
&world.array_proto,
|
||||
&world.function_proto,
|
||||
&world.string_proto,
|
||||
@@ -410,6 +430,16 @@ pub(crate) fn wire_builtin_world_links(world: &BuiltinWorld) {
|
||||
&world.disposable_stack_proto,
|
||||
&world.async_disposable_stack_proto,
|
||||
&world.async_iterator_proto,
|
||||
&world.number_format_proto,
|
||||
&world.date_time_format_proto,
|
||||
&world.collator_proto,
|
||||
&world.plural_rules_proto,
|
||||
&world.list_format_proto,
|
||||
&world.relative_time_format_proto,
|
||||
&world.display_names_proto,
|
||||
&world.segmenter_proto,
|
||||
&world.locale_proto,
|
||||
&world.duration_format_proto,
|
||||
];
|
||||
for proto in &non_object_protos {
|
||||
set_proto_if_changed(proto, obj_proto_val);
|
||||
@@ -419,6 +449,9 @@ pub(crate) fn wire_builtin_world_links(world: &BuiltinWorld) {
|
||||
set_proto_if_changed(&world.temporal_object, obj_proto_val);
|
||||
set_proto_if_changed(&world.temporal_now_object, obj_proto_val);
|
||||
|
||||
// Intl 命名空间对象(非构造器)继承 Object.prototype。
|
||||
set_proto_if_changed(&world.intl_object, obj_proto_val);
|
||||
|
||||
// Console 对象(单例命名空间)继承 Object.prototype。
|
||||
set_proto_if_changed(&world.console_object, obj_proto_val);
|
||||
|
||||
@@ -569,6 +602,28 @@ impl BuiltinWorld {
|
||||
let (bigint_proto, bigint_constructor) = make_named_pair(string_forge, shape_forge, labels, "BigInt");
|
||||
let stub_objects = Vec::new();
|
||||
|
||||
// Intl 命名空间对象(非构造器空对象)与十组构造器/原型对:骨架阶段
|
||||
// 构造语义(实例化/resolvedOptions/静态方法)由绑定层 native 填充,
|
||||
// 本处只预留 P 字段使快照/脏检查/收尾枚举覆盖到这二十一枚对象。
|
||||
let intl_object = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
|
||||
let (number_format_proto, number_format_constructor) =
|
||||
make_named_pair(string_forge, shape_forge, labels, "NumberFormat");
|
||||
let (date_time_format_proto, date_time_format_constructor) =
|
||||
make_named_pair(string_forge, shape_forge, labels, "DateTimeFormat");
|
||||
let (collator_proto, collator_constructor) = make_named_pair(string_forge, shape_forge, labels, "Collator");
|
||||
let (plural_rules_proto, plural_rules_constructor) =
|
||||
make_named_pair(string_forge, shape_forge, labels, "PluralRules");
|
||||
let (list_format_proto, list_format_constructor) =
|
||||
make_named_pair(string_forge, shape_forge, labels, "ListFormat");
|
||||
let (relative_time_format_proto, relative_time_format_constructor) =
|
||||
make_named_pair(string_forge, shape_forge, labels, "RelativeTimeFormat");
|
||||
let (display_names_proto, display_names_constructor) =
|
||||
make_named_pair(string_forge, shape_forge, labels, "DisplayNames");
|
||||
let (segmenter_proto, segmenter_constructor) = make_named_pair(string_forge, shape_forge, labels, "Segmenter");
|
||||
let (locale_proto, locale_constructor) = make_named_pair(string_forge, shape_forge, labels, "Locale");
|
||||
let (duration_format_proto, duration_format_constructor) =
|
||||
make_named_pair(string_forge, shape_forge, labels, "DurationFormat");
|
||||
|
||||
// 迭代器原型家族:链关系(→ %IteratorPrototype% → Object.prototype)在
|
||||
// wire_builtin_world_links 中建立,next/@@iterator 方法由绑定层
|
||||
// (oxide_builtins 中安装方法 wrapper 的代码)安装。
|
||||
@@ -699,6 +754,27 @@ impl BuiltinWorld {
|
||||
custom_event_proto,
|
||||
custom_event_constructor,
|
||||
event_target_proto,
|
||||
intl_object,
|
||||
number_format_constructor,
|
||||
number_format_proto,
|
||||
date_time_format_constructor,
|
||||
date_time_format_proto,
|
||||
collator_constructor,
|
||||
collator_proto,
|
||||
plural_rules_constructor,
|
||||
plural_rules_proto,
|
||||
list_format_constructor,
|
||||
list_format_proto,
|
||||
relative_time_format_constructor,
|
||||
relative_time_format_proto,
|
||||
display_names_constructor,
|
||||
display_names_proto,
|
||||
segmenter_constructor,
|
||||
segmenter_proto,
|
||||
locale_constructor,
|
||||
locale_proto,
|
||||
duration_format_constructor,
|
||||
duration_format_proto,
|
||||
leaked_objects: std::cell::RefCell::new(Vec::new()),
|
||||
};
|
||||
wire_builtin_world_links(&world);
|
||||
|
||||
@@ -460,6 +460,98 @@ impl BuiltinWorld {
|
||||
)
|
||||
};
|
||||
|
||||
// Intl 命名空间对象(非构造器空对象)与十组构造器/原型对:脏则换新
|
||||
// 空对象占位(与全量构造同形),未脏沿用旧指针。
|
||||
let (
|
||||
intl_object,
|
||||
number_format_constructor,
|
||||
number_format_proto,
|
||||
date_time_format_constructor,
|
||||
date_time_format_proto,
|
||||
collator_constructor,
|
||||
collator_proto,
|
||||
plural_rules_constructor,
|
||||
plural_rules_proto,
|
||||
list_format_constructor,
|
||||
list_format_proto,
|
||||
relative_time_format_constructor,
|
||||
relative_time_format_proto,
|
||||
display_names_constructor,
|
||||
display_names_proto,
|
||||
segmenter_constructor,
|
||||
segmenter_proto,
|
||||
locale_constructor,
|
||||
locale_proto,
|
||||
duration_format_constructor,
|
||||
duration_format_proto,
|
||||
) = if dirty.intl {
|
||||
let (number_format_proto, number_format_constructor) =
|
||||
make_named_pair(string_forge, shape_forge, labels, "NumberFormat");
|
||||
let (date_time_format_proto, date_time_format_constructor) =
|
||||
make_named_pair(string_forge, shape_forge, labels, "DateTimeFormat");
|
||||
let (collator_proto, collator_constructor) = make_named_pair(string_forge, shape_forge, labels, "Collator");
|
||||
let (plural_rules_proto, plural_rules_constructor) =
|
||||
make_named_pair(string_forge, shape_forge, labels, "PluralRules");
|
||||
let (list_format_proto, list_format_constructor) =
|
||||
make_named_pair(string_forge, shape_forge, labels, "ListFormat");
|
||||
let (relative_time_format_proto, relative_time_format_constructor) =
|
||||
make_named_pair(string_forge, shape_forge, labels, "RelativeTimeFormat");
|
||||
let (display_names_proto, display_names_constructor) =
|
||||
make_named_pair(string_forge, shape_forge, labels, "DisplayNames");
|
||||
let (segmenter_proto, segmenter_constructor) =
|
||||
make_named_pair(string_forge, shape_forge, labels, "Segmenter");
|
||||
let (locale_proto, locale_constructor) = make_named_pair(string_forge, shape_forge, labels, "Locale");
|
||||
let (duration_format_proto, duration_format_constructor) =
|
||||
make_named_pair(string_forge, shape_forge, labels, "DurationFormat");
|
||||
(
|
||||
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||
number_format_constructor,
|
||||
number_format_proto,
|
||||
date_time_format_constructor,
|
||||
date_time_format_proto,
|
||||
collator_constructor,
|
||||
collator_proto,
|
||||
plural_rules_constructor,
|
||||
plural_rules_proto,
|
||||
list_format_constructor,
|
||||
list_format_proto,
|
||||
relative_time_format_constructor,
|
||||
relative_time_format_proto,
|
||||
display_names_constructor,
|
||||
display_names_proto,
|
||||
segmenter_constructor,
|
||||
segmenter_proto,
|
||||
locale_constructor,
|
||||
locale_proto,
|
||||
duration_format_constructor,
|
||||
duration_format_proto,
|
||||
)
|
||||
} else {
|
||||
(
|
||||
current.intl_object.clone(),
|
||||
current.number_format_constructor.clone(),
|
||||
current.number_format_proto.clone(),
|
||||
current.date_time_format_constructor.clone(),
|
||||
current.date_time_format_proto.clone(),
|
||||
current.collator_constructor.clone(),
|
||||
current.collator_proto.clone(),
|
||||
current.plural_rules_constructor.clone(),
|
||||
current.plural_rules_proto.clone(),
|
||||
current.list_format_constructor.clone(),
|
||||
current.list_format_proto.clone(),
|
||||
current.relative_time_format_constructor.clone(),
|
||||
current.relative_time_format_proto.clone(),
|
||||
current.display_names_constructor.clone(),
|
||||
current.display_names_proto.clone(),
|
||||
current.segmenter_constructor.clone(),
|
||||
current.segmenter_proto.clone(),
|
||||
current.locale_constructor.clone(),
|
||||
current.locale_proto.clone(),
|
||||
current.duration_format_constructor.clone(),
|
||||
current.duration_format_proto.clone(),
|
||||
)
|
||||
};
|
||||
|
||||
// 迭代器原型与资源栈原型依赖 Object.prototype(链到其上):object 家族重建时
|
||||
// 一并重建,否则旧原型链指向已释放的 object_proto。
|
||||
let (
|
||||
@@ -623,6 +715,27 @@ impl BuiltinWorld {
|
||||
custom_event_proto,
|
||||
custom_event_constructor,
|
||||
event_target_proto,
|
||||
intl_object,
|
||||
number_format_constructor,
|
||||
number_format_proto,
|
||||
date_time_format_constructor,
|
||||
date_time_format_proto,
|
||||
collator_constructor,
|
||||
collator_proto,
|
||||
plural_rules_constructor,
|
||||
plural_rules_proto,
|
||||
list_format_constructor,
|
||||
list_format_proto,
|
||||
relative_time_format_constructor,
|
||||
relative_time_format_proto,
|
||||
display_names_constructor,
|
||||
display_names_proto,
|
||||
segmenter_constructor,
|
||||
segmenter_proto,
|
||||
locale_constructor,
|
||||
locale_proto,
|
||||
duration_format_constructor,
|
||||
duration_format_proto,
|
||||
leaked_objects: std::cell::RefCell::new(Vec::new()),
|
||||
};
|
||||
wire_builtin_world_links(&world);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//! 注册表职责:BuiltinWorld 结构(108 固定 P 字段 + stub 族 + Box::into_raw
|
||||
//! 登记表)、get_by_id 派发、all_p_fields 108 元组枚举(新增 P 字段四处同步
|
||||
//! 注册表职责:BuiltinWorld 结构(129 固定 P 字段 + stub 族 + Box::into_raw
|
||||
//! 登记表)、get_by_id 派发、all_p_fields 129 元组枚举(新增 P 字段四处同步
|
||||
//! 约束载体)与登记表 track/find/inherit/teardown。
|
||||
|
||||
use oxide_types::mem::P;
|
||||
@@ -177,6 +177,40 @@ pub struct BuiltinWorld {
|
||||
/// `EventTarget.prototype` 槽位:纯内核接线占位(空对象),真实原型
|
||||
/// 本体由绑定层后续任务填充(EventTarget 无构造器,仅暴露原型)。
|
||||
pub event_target_proto: P<JsObject>,
|
||||
/// `Intl` 命名空间对象(非构造器):构造器/原型对与静态方法由绑定层
|
||||
/// 安装;脏家族位为 `intl`。
|
||||
pub intl_object: P<JsObject>,
|
||||
/// `Intl.NumberFormat` 构造器与原型对:经 `make_named_pair` 成对构造,
|
||||
/// 骨架阶段构造语义(实例化/resolvedOptions)由绑定层 native 填充。
|
||||
pub number_format_constructor: P<JsObject>,
|
||||
pub number_format_proto: P<JsObject>,
|
||||
/// `Intl.DateTimeFormat` 构造器与原型对。
|
||||
pub date_time_format_constructor: P<JsObject>,
|
||||
pub date_time_format_proto: P<JsObject>,
|
||||
/// `Intl.Collator` 构造器与原型对。
|
||||
pub collator_constructor: P<JsObject>,
|
||||
pub collator_proto: P<JsObject>,
|
||||
/// `Intl.PluralRules` 构造器与原型对。
|
||||
pub plural_rules_constructor: P<JsObject>,
|
||||
pub plural_rules_proto: P<JsObject>,
|
||||
/// `Intl.ListFormat` 构造器与原型对。
|
||||
pub list_format_constructor: P<JsObject>,
|
||||
pub list_format_proto: P<JsObject>,
|
||||
/// `Intl.RelativeTimeFormat` 构造器与原型对。
|
||||
pub relative_time_format_constructor: P<JsObject>,
|
||||
pub relative_time_format_proto: P<JsObject>,
|
||||
/// `Intl.DisplayNames` 构造器与原型对。
|
||||
pub display_names_constructor: P<JsObject>,
|
||||
pub display_names_proto: P<JsObject>,
|
||||
/// `Intl.Segmenter` 构造器与原型对。
|
||||
pub segmenter_constructor: P<JsObject>,
|
||||
pub segmenter_proto: P<JsObject>,
|
||||
/// `Intl.Locale` 构造器与原型对。
|
||||
pub locale_constructor: P<JsObject>,
|
||||
pub locale_proto: P<JsObject>,
|
||||
/// `Intl.DurationFormat` 构造器与原型对。
|
||||
pub duration_format_constructor: P<JsObject>,
|
||||
pub duration_format_proto: P<JsObject>,
|
||||
/// 释放登记表(`Box::into_raw` 对象的清单,session 收尾统一释放,非内存泄漏):
|
||||
/// 绑定层经 `Box::into_raw` 持有的函数/宿主对象(方法 wrapper、访问器、
|
||||
/// 错误构造器、Reflect/Iterator、内建原型构造器、`$262` 宿主等)。
|
||||
@@ -407,7 +441,7 @@ impl BuiltinWorld {
|
||||
/// session 收尾(`teardown_heap_data`)与选择性重建收尾(`retire_replaced`)
|
||||
/// 的 P 字段枚举唯一入口:`BuiltinWorld` 新增 P 字段须在此同步补一行,否则
|
||||
/// 收尾时该字段属性区无法释放、重建原型槽改写/释放漏掉该字段。
|
||||
pub(crate) fn all_p_fields(&self) -> [&P<JsObject>; 108] {
|
||||
pub(crate) fn all_p_fields(&self) -> [&P<JsObject>; 129] {
|
||||
[
|
||||
&self.object_proto,
|
||||
&self.array_proto,
|
||||
@@ -517,6 +551,27 @@ impl BuiltinWorld {
|
||||
&self.custom_event_proto,
|
||||
&self.custom_event_constructor,
|
||||
&self.event_target_proto,
|
||||
&self.intl_object,
|
||||
&self.number_format_constructor,
|
||||
&self.number_format_proto,
|
||||
&self.date_time_format_constructor,
|
||||
&self.date_time_format_proto,
|
||||
&self.collator_constructor,
|
||||
&self.collator_proto,
|
||||
&self.plural_rules_constructor,
|
||||
&self.plural_rules_proto,
|
||||
&self.list_format_constructor,
|
||||
&self.list_format_proto,
|
||||
&self.relative_time_format_constructor,
|
||||
&self.relative_time_format_proto,
|
||||
&self.display_names_constructor,
|
||||
&self.display_names_proto,
|
||||
&self.segmenter_constructor,
|
||||
&self.segmenter_proto,
|
||||
&self.locale_constructor,
|
||||
&self.locale_proto,
|
||||
&self.duration_format_constructor,
|
||||
&self.duration_format_proto,
|
||||
]
|
||||
}
|
||||
|
||||
@@ -663,6 +718,27 @@ impl BuiltinWorld {
|
||||
BuiltinId::CustomEventProto => &self.custom_event_proto,
|
||||
BuiltinId::CustomEventCtor => &self.custom_event_constructor,
|
||||
BuiltinId::EventTargetProto => &self.event_target_proto,
|
||||
BuiltinId::IntlObject => &self.intl_object,
|
||||
BuiltinId::NumberFormatConstructor => &self.number_format_constructor,
|
||||
BuiltinId::NumberFormatProto => &self.number_format_proto,
|
||||
BuiltinId::DateTimeFormatConstructor => &self.date_time_format_constructor,
|
||||
BuiltinId::DateTimeFormatProto => &self.date_time_format_proto,
|
||||
BuiltinId::CollatorConstructor => &self.collator_constructor,
|
||||
BuiltinId::CollatorProto => &self.collator_proto,
|
||||
BuiltinId::PluralRulesConstructor => &self.plural_rules_constructor,
|
||||
BuiltinId::PluralRulesProto => &self.plural_rules_proto,
|
||||
BuiltinId::ListFormatConstructor => &self.list_format_constructor,
|
||||
BuiltinId::ListFormatProto => &self.list_format_proto,
|
||||
BuiltinId::RelativeTimeFormatConstructor => &self.relative_time_format_constructor,
|
||||
BuiltinId::RelativeTimeFormatProto => &self.relative_time_format_proto,
|
||||
BuiltinId::DisplayNamesConstructor => &self.display_names_constructor,
|
||||
BuiltinId::DisplayNamesProto => &self.display_names_proto,
|
||||
BuiltinId::SegmenterConstructor => &self.segmenter_constructor,
|
||||
BuiltinId::SegmenterProto => &self.segmenter_proto,
|
||||
BuiltinId::LocaleConstructor => &self.locale_constructor,
|
||||
BuiltinId::LocaleProto => &self.locale_proto,
|
||||
BuiltinId::DurationFormatConstructor => &self.duration_format_constructor,
|
||||
BuiltinId::DurationFormatProto => &self.duration_format_proto,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! 内置对象 id 枚举(`BuiltinId` 98 变体 + `ALL` 顺序钉表)、世代快照
|
||||
//! 内置对象 id 枚举(`BuiltinId` 119 变体 + `ALL` 顺序钉表)、世代快照
|
||||
//! (`BuiltinSnapshot`)与按家族划分的脏标记位集(`BuiltinDirtySet`);
|
||||
//! `NUM_BUILTINS` 文档承载"新增 BuiltinWorld 字段须同步"四处约束注记。
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::builtin::BuiltinWorld;
|
||||
/// 维护注意:每个新增的 `BuiltinWorld` 对象字段都必须加到这里以及
|
||||
/// `KernelSession::dirty_since_snapshot()`,以便选择性重置重建正确的
|
||||
/// builtin 家族。
|
||||
pub const NUM_BUILTINS: usize = 98;
|
||||
pub const NUM_BUILTINS: usize = 119;
|
||||
|
||||
/// 内置对象枚举 id,与 `BuiltinWorld` 中的存储槽一一对应。
|
||||
///
|
||||
@@ -118,6 +118,27 @@ pub enum BuiltinId {
|
||||
CustomEventProto = 95,
|
||||
CustomEventCtor = 96,
|
||||
EventTargetProto = 97,
|
||||
IntlObject = 98,
|
||||
NumberFormatConstructor = 99,
|
||||
NumberFormatProto = 100,
|
||||
DateTimeFormatConstructor = 101,
|
||||
DateTimeFormatProto = 102,
|
||||
CollatorConstructor = 103,
|
||||
CollatorProto = 104,
|
||||
PluralRulesConstructor = 105,
|
||||
PluralRulesProto = 106,
|
||||
ListFormatConstructor = 107,
|
||||
ListFormatProto = 108,
|
||||
RelativeTimeFormatConstructor = 109,
|
||||
RelativeTimeFormatProto = 110,
|
||||
DisplayNamesConstructor = 111,
|
||||
DisplayNamesProto = 112,
|
||||
SegmenterConstructor = 113,
|
||||
SegmenterProto = 114,
|
||||
LocaleConstructor = 115,
|
||||
LocaleProto = 116,
|
||||
DurationFormatConstructor = 117,
|
||||
DurationFormatProto = 118,
|
||||
}
|
||||
|
||||
impl BuiltinId {
|
||||
@@ -223,6 +244,27 @@ impl BuiltinId {
|
||||
BuiltinId::CustomEventProto,
|
||||
BuiltinId::CustomEventCtor,
|
||||
BuiltinId::EventTargetProto,
|
||||
BuiltinId::IntlObject,
|
||||
BuiltinId::NumberFormatConstructor,
|
||||
BuiltinId::NumberFormatProto,
|
||||
BuiltinId::DateTimeFormatConstructor,
|
||||
BuiltinId::DateTimeFormatProto,
|
||||
BuiltinId::CollatorConstructor,
|
||||
BuiltinId::CollatorProto,
|
||||
BuiltinId::PluralRulesConstructor,
|
||||
BuiltinId::PluralRulesProto,
|
||||
BuiltinId::ListFormatConstructor,
|
||||
BuiltinId::ListFormatProto,
|
||||
BuiltinId::RelativeTimeFormatConstructor,
|
||||
BuiltinId::RelativeTimeFormatProto,
|
||||
BuiltinId::DisplayNamesConstructor,
|
||||
BuiltinId::DisplayNamesProto,
|
||||
BuiltinId::SegmenterConstructor,
|
||||
BuiltinId::SegmenterProto,
|
||||
BuiltinId::LocaleConstructor,
|
||||
BuiltinId::LocaleProto,
|
||||
BuiltinId::DurationFormatConstructor,
|
||||
BuiltinId::DurationFormatProto,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -301,6 +343,7 @@ pub struct BuiltinDirtySet {
|
||||
pub message_channel: bool,
|
||||
pub broadcast_channel: bool,
|
||||
pub event: bool,
|
||||
pub intl: bool,
|
||||
}
|
||||
|
||||
impl BuiltinDirtySet {
|
||||
@@ -337,6 +380,7 @@ impl BuiltinDirtySet {
|
||||
message_channel: true,
|
||||
broadcast_channel: true,
|
||||
event: true,
|
||||
intl: true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,6 +411,7 @@ impl BuiltinDirtySet {
|
||||
|| self.message_channel
|
||||
|| self.broadcast_channel
|
||||
|| self.event
|
||||
|| self.intl
|
||||
}
|
||||
|
||||
/// 是否存在任何污染(builtin world 或 global object)。
|
||||
@@ -379,13 +424,13 @@ impl BuiltinDirtySet {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// 槽对齐面:ALL 表与判别值 0..97 严格同序,快照数组随 NUM_BUILTINS
|
||||
/// 槽对齐面:ALL 表与判别值 0..118 严格同序,快照数组随 NUM_BUILTINS
|
||||
/// 自动扩维、逐槽对齐。
|
||||
#[test]
|
||||
fn builtin_snapshot_all_slots_aligned() {
|
||||
assert_eq!(NUM_BUILTINS, 98);
|
||||
assert_eq!(NUM_BUILTINS, 119);
|
||||
assert_eq!(BuiltinId::ALL.len(), NUM_BUILTINS);
|
||||
for i in 0..97usize {
|
||||
for i in 0..118usize {
|
||||
assert_eq!(BuiltinId::ALL[i] as usize, i);
|
||||
}
|
||||
assert_eq!(BuiltinId::ALL[81], BuiltinId::SharedArrayBufferProto);
|
||||
|
||||
@@ -209,6 +209,27 @@ impl KernelSession {
|
||||
|| gen(BuiltinId::CustomEventProto) != snap(BuiltinId::CustomEventProto)
|
||||
|| gen(BuiltinId::CustomEventCtor) != snap(BuiltinId::CustomEventCtor)
|
||||
|| gen(BuiltinId::EventTargetProto) != snap(BuiltinId::EventTargetProto),
|
||||
intl: gen(BuiltinId::IntlObject) != snap(BuiltinId::IntlObject)
|
||||
|| gen(BuiltinId::NumberFormatConstructor) != snap(BuiltinId::NumberFormatConstructor)
|
||||
|| gen(BuiltinId::NumberFormatProto) != snap(BuiltinId::NumberFormatProto)
|
||||
|| gen(BuiltinId::DateTimeFormatConstructor) != snap(BuiltinId::DateTimeFormatConstructor)
|
||||
|| gen(BuiltinId::DateTimeFormatProto) != snap(BuiltinId::DateTimeFormatProto)
|
||||
|| gen(BuiltinId::CollatorConstructor) != snap(BuiltinId::CollatorConstructor)
|
||||
|| gen(BuiltinId::CollatorProto) != snap(BuiltinId::CollatorProto)
|
||||
|| gen(BuiltinId::PluralRulesConstructor) != snap(BuiltinId::PluralRulesConstructor)
|
||||
|| gen(BuiltinId::PluralRulesProto) != snap(BuiltinId::PluralRulesProto)
|
||||
|| gen(BuiltinId::ListFormatConstructor) != snap(BuiltinId::ListFormatConstructor)
|
||||
|| gen(BuiltinId::ListFormatProto) != snap(BuiltinId::ListFormatProto)
|
||||
|| gen(BuiltinId::RelativeTimeFormatConstructor) != snap(BuiltinId::RelativeTimeFormatConstructor)
|
||||
|| gen(BuiltinId::RelativeTimeFormatProto) != snap(BuiltinId::RelativeTimeFormatProto)
|
||||
|| gen(BuiltinId::DisplayNamesConstructor) != snap(BuiltinId::DisplayNamesConstructor)
|
||||
|| gen(BuiltinId::DisplayNamesProto) != snap(BuiltinId::DisplayNamesProto)
|
||||
|| gen(BuiltinId::SegmenterConstructor) != snap(BuiltinId::SegmenterConstructor)
|
||||
|| gen(BuiltinId::SegmenterProto) != snap(BuiltinId::SegmenterProto)
|
||||
|| gen(BuiltinId::LocaleConstructor) != snap(BuiltinId::LocaleConstructor)
|
||||
|| gen(BuiltinId::LocaleProto) != snap(BuiltinId::LocaleProto)
|
||||
|| gen(BuiltinId::DurationFormatConstructor) != snap(BuiltinId::DurationFormatConstructor)
|
||||
|| gen(BuiltinId::DurationFormatProto) != snap(BuiltinId::DurationFormatProto),
|
||||
};
|
||||
// wrapper 本体被写不落在任何家族位上(写的是 wrapper 自身而非所属 P
|
||||
// 对象):收缩为全脏,强制重建全部家族与 global,使被写的可复用
|
||||
|
||||
@@ -122,6 +122,28 @@ pub enum ProtoKind {
|
||||
MessageEventProto,
|
||||
ErrorEventProto,
|
||||
CustomEventProto,
|
||||
// Intl 十组构造器与原型(Intl 命名空间对象不占 ProtoKind:builtins 不读它,
|
||||
// 绑定层直接访问 world 的 intl_object 槽)
|
||||
NumberFormatConstructor,
|
||||
NumberFormatProto,
|
||||
DateTimeFormatConstructor,
|
||||
DateTimeFormatProto,
|
||||
CollatorConstructor,
|
||||
CollatorProto,
|
||||
PluralRulesConstructor,
|
||||
PluralRulesProto,
|
||||
ListFormatConstructor,
|
||||
ListFormatProto,
|
||||
RelativeTimeFormatConstructor,
|
||||
RelativeTimeFormatProto,
|
||||
DisplayNamesConstructor,
|
||||
DisplayNamesProto,
|
||||
SegmenterConstructor,
|
||||
SegmenterProto,
|
||||
LocaleConstructor,
|
||||
LocaleProto,
|
||||
DurationFormatConstructor,
|
||||
DurationFormatProto,
|
||||
}
|
||||
|
||||
/// builtins 依赖的 `Vm` 能力集合。
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
use crate::bindings::{
|
||||
apply_binding_table, bind_global_value, bind_well_known_data_property, configure_native_constructor, BindEnv,
|
||||
};
|
||||
use oxide_types::object::{JsObject, PropAttributes};
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
/// 一组 Intl 构造器/原型对:(构造器槽,原型槽,构造器 native 函数,
|
||||
/// 构造器 length 槽,命名空间槽名,原型标签)。
|
||||
type IntlCtorPair<'a> = (
|
||||
&'a oxide_types::mem::P<JsObject>,
|
||||
&'a oxide_types::mem::P<JsObject>,
|
||||
*const (),
|
||||
u32,
|
||||
&'a str,
|
||||
&'a str,
|
||||
);
|
||||
|
||||
/// 把 Intl 命名空间对象与十组构造器/原型对绑定到 global。
|
||||
///
|
||||
/// `realm_id` 是目标所属 realm 的编号:`@@toStringTag` 符号键按 (realm 编号,
|
||||
/// 局部下标) 编码,realm 编号为 0 时与旧编码逐字节一致。
|
||||
///
|
||||
/// 构造器/原型对与 `prototype`/`constructor` 互指、命名空间与构造器的
|
||||
/// `[[Prototype]]` 链由内核接线(`wire_builtin_world_links`)预置,本函数
|
||||
/// 只安装 native 函数面、`length` 与 `@@toStringTag`。
|
||||
pub fn bind_intl(env: BindEnv<'_>, global: &mut JsObject) {
|
||||
let world = env.world();
|
||||
let intl_ptr = world.intl_object.as_ptr() as *mut JsObject;
|
||||
let intl = unsafe { &mut *intl_ptr };
|
||||
|
||||
// Intl.getCanonicalLocales 与 Intl.supportedValuesOf:命名空间对象上的
|
||||
// 静态方法(各 length 1,非构造器)。
|
||||
apply_binding_table(
|
||||
env,
|
||||
intl,
|
||||
&[
|
||||
(
|
||||
"getCanonicalLocales",
|
||||
oxide_builtins::intl::get_canonical_locales::<crate::vm::Vm> as *const (),
|
||||
1,
|
||||
),
|
||||
(
|
||||
"supportedValuesOf",
|
||||
oxide_builtins::intl::supported_values_of::<crate::vm::Vm> as *const (),
|
||||
1,
|
||||
),
|
||||
],
|
||||
);
|
||||
bind_well_known_data_property(
|
||||
env.core,
|
||||
intl,
|
||||
9,
|
||||
JsValue::perm_string(env.string_forge().string_ptr(env.string_forge().intern("Intl").0)),
|
||||
PropAttributes::new(false, false, true),
|
||||
env.realm_id,
|
||||
);
|
||||
|
||||
// 十组构造器/原型对:构造器(length 0)+ supportedLocalesOf 静态方法 +
|
||||
// 命名空间槽(Intl.NumberFormat 等)+ 原型 @@toStringTag。
|
||||
let pairs: [IntlCtorPair; 10] = [
|
||||
(
|
||||
&world.number_format_constructor,
|
||||
&world.number_format_proto,
|
||||
oxide_builtins::intl::number_format_constructor::<crate::vm::Vm> as *const (),
|
||||
0,
|
||||
"NumberFormat",
|
||||
"Intl.NumberFormat",
|
||||
),
|
||||
(
|
||||
&world.date_time_format_constructor,
|
||||
&world.date_time_format_proto,
|
||||
oxide_builtins::intl::date_time_format_constructor::<crate::vm::Vm> as *const (),
|
||||
0,
|
||||
"DateTimeFormat",
|
||||
"Intl.DateTimeFormat",
|
||||
),
|
||||
(
|
||||
&world.collator_constructor,
|
||||
&world.collator_proto,
|
||||
oxide_builtins::intl::collator_constructor::<crate::vm::Vm> as *const (),
|
||||
0,
|
||||
"Collator",
|
||||
"Intl.Collator",
|
||||
),
|
||||
(
|
||||
&world.plural_rules_constructor,
|
||||
&world.plural_rules_proto,
|
||||
oxide_builtins::intl::plural_rules_constructor::<crate::vm::Vm> as *const (),
|
||||
0,
|
||||
"PluralRules",
|
||||
"Intl.PluralRules",
|
||||
),
|
||||
(
|
||||
&world.list_format_constructor,
|
||||
&world.list_format_proto,
|
||||
oxide_builtins::intl::list_format_constructor::<crate::vm::Vm> as *const (),
|
||||
0,
|
||||
"ListFormat",
|
||||
"Intl.ListFormat",
|
||||
),
|
||||
(
|
||||
&world.relative_time_format_constructor,
|
||||
&world.relative_time_format_proto,
|
||||
oxide_builtins::intl::relative_time_format_constructor::<crate::vm::Vm> as *const (),
|
||||
0,
|
||||
"RelativeTimeFormat",
|
||||
"Intl.RelativeTimeFormat",
|
||||
),
|
||||
(
|
||||
&world.display_names_constructor,
|
||||
&world.display_names_proto,
|
||||
oxide_builtins::intl::display_names_constructor::<crate::vm::Vm> as *const (),
|
||||
2,
|
||||
"DisplayNames",
|
||||
"Intl.DisplayNames",
|
||||
),
|
||||
(
|
||||
&world.segmenter_constructor,
|
||||
&world.segmenter_proto,
|
||||
oxide_builtins::intl::segmenter_constructor::<crate::vm::Vm> as *const (),
|
||||
0,
|
||||
"Segmenter",
|
||||
"Intl.Segmenter",
|
||||
),
|
||||
(
|
||||
&world.locale_constructor,
|
||||
&world.locale_proto,
|
||||
oxide_builtins::intl::locale_constructor::<crate::vm::Vm> as *const (),
|
||||
1,
|
||||
"Locale",
|
||||
"Intl.Locale",
|
||||
),
|
||||
(
|
||||
&world.duration_format_constructor,
|
||||
&world.duration_format_proto,
|
||||
oxide_builtins::intl::duration_format_constructor::<crate::vm::Vm> as *const (),
|
||||
0,
|
||||
"DurationFormat",
|
||||
"Intl.DurationFormat",
|
||||
),
|
||||
];
|
||||
for (ctor_p, proto_p, ctor_fn, length, name, tag) in pairs {
|
||||
let ctor = unsafe { &mut *(ctor_p.as_ptr() as *mut JsObject) };
|
||||
let proto = unsafe { &mut *(proto_p.as_ptr() as *mut JsObject) };
|
||||
bind_intl_ctor_pair(env, ctor, proto, ctor_fn, length, tag);
|
||||
// 命名空间对象上的构造器槽(如 Intl.NumberFormat):既有槽原位更新。
|
||||
bind_global_value(env.core, intl, name, JsValue::from_js_object(ctor_p.as_ptr() as *mut JsObject));
|
||||
}
|
||||
|
||||
// 命名空间对象挂到 global(既有槽原位更新,幂等)。
|
||||
bind_global_value(env.core, global, "Intl", JsValue::from_js_object(intl_ptr));
|
||||
}
|
||||
|
||||
/// 安装一组 Intl 构造器/原型对:构造器 native 函数与 `length`、
|
||||
/// `supportedLocalesOf` 静态方法、原型 `@@toStringTag`。
|
||||
fn bind_intl_ctor_pair(
|
||||
env: BindEnv<'_>,
|
||||
ctor: &mut JsObject,
|
||||
proto: &mut JsObject,
|
||||
ctor_fn: *const (),
|
||||
length: u32,
|
||||
tag: &str,
|
||||
) {
|
||||
// 构造器:length 槽的 native 函数;`prototype`/`name` 槽由内核接线
|
||||
// 预置(wire_ctor_proto / make_pair),此处不重复建槽。
|
||||
configure_native_constructor(ctor, ctor_fn, 0);
|
||||
let length_si = env.string_forge().intern("length").0;
|
||||
let length_shape = env.shape_forge().make_shape(ctor.shape_id(), length_si);
|
||||
ctor.set_shape_id(length_shape);
|
||||
ctor.ensure_hash_props().push(JsValue::int(length as i32));
|
||||
let length_pos = ctor.hash_props_vec().map_or(0, |props| props.len() as u32).saturating_sub(1);
|
||||
ctor.set_data_meta(length_pos, PropAttributes::new(false, false, true));
|
||||
|
||||
// supportedLocalesOf:普通静态方法(非构造器、无 prototype),length 1。
|
||||
apply_binding_table(
|
||||
env,
|
||||
ctor,
|
||||
&[(
|
||||
"supportedLocalesOf",
|
||||
oxide_builtins::intl::supported_locales_of::<crate::vm::Vm> as *const (),
|
||||
1,
|
||||
)],
|
||||
);
|
||||
|
||||
// 原型 @@toStringTag:{ writable:false, enumerable:false, configurable:true }。
|
||||
bind_well_known_data_property(
|
||||
env.core,
|
||||
proto,
|
||||
9,
|
||||
JsValue::perm_string(env.string_forge().string_ptr(env.string_forge().intern(tag).0)),
|
||||
PropAttributes::new(false, false, true),
|
||||
env.realm_id,
|
||||
);
|
||||
}
|
||||
@@ -33,6 +33,8 @@ pub mod bind_event;
|
||||
pub mod bind_function;
|
||||
/// global 对象上的普通全局函数(parseInt、isNaN 等)绑定。
|
||||
pub mod bind_global;
|
||||
/// Intl 命名空间对象与十组构造器/原型的 native 绑定(骨架阶段)。
|
||||
pub mod bind_intl;
|
||||
/// Iterator 相关全局辅助对象(%IteratorPrototype% 等)绑定。
|
||||
pub mod bind_iterator;
|
||||
/// JSON 单例对象及其 native 方法绑定。
|
||||
@@ -1118,6 +1120,7 @@ pub fn bind_global_builtin_slots(
|
||||
("JSON", JsValue::from_js_object(world.json_object.as_ptr() as *mut JsObject)),
|
||||
("Temporal", JsValue::from_js_object(world.temporal_object.as_ptr() as *mut JsObject)),
|
||||
("Atomics", JsValue::from_js_object(world.atomics_object.as_ptr() as *mut JsObject)),
|
||||
("Intl", JsValue::from_js_object(world.intl_object.as_ptr() as *mut JsObject)),
|
||||
] {
|
||||
bind_existing_global(core, global, name, value);
|
||||
}
|
||||
@@ -1343,6 +1346,9 @@ pub fn rebind_dirty_builtins(
|
||||
if dirty.map_or(true, |d| d.temporal) {
|
||||
bind_temporal::bind_temporal(env, global);
|
||||
}
|
||||
if dirty.map_or(true, |d| d.intl) {
|
||||
bind_intl::bind_intl(env, global);
|
||||
}
|
||||
// 独占函数(bind_stubs / bind_weak_map)需要 session 的可变借用,与 env
|
||||
// 的共享借用冲突;env 的最后一次使用在上面的 temporal 分支,独占调用
|
||||
// 之后重建 env 供后续分支使用。
|
||||
|
||||
@@ -148,6 +148,26 @@ impl oxide_runtime_api::VmHost for Vm {
|
||||
ProtoKind::MessageEventProto => &world.message_event_proto,
|
||||
ProtoKind::ErrorEventProto => &world.error_event_proto,
|
||||
ProtoKind::CustomEventProto => &world.custom_event_proto,
|
||||
ProtoKind::NumberFormatConstructor => &world.number_format_constructor,
|
||||
ProtoKind::NumberFormatProto => &world.number_format_proto,
|
||||
ProtoKind::DateTimeFormatConstructor => &world.date_time_format_constructor,
|
||||
ProtoKind::DateTimeFormatProto => &world.date_time_format_proto,
|
||||
ProtoKind::CollatorConstructor => &world.collator_constructor,
|
||||
ProtoKind::CollatorProto => &world.collator_proto,
|
||||
ProtoKind::PluralRulesConstructor => &world.plural_rules_constructor,
|
||||
ProtoKind::PluralRulesProto => &world.plural_rules_proto,
|
||||
ProtoKind::ListFormatConstructor => &world.list_format_constructor,
|
||||
ProtoKind::ListFormatProto => &world.list_format_proto,
|
||||
ProtoKind::RelativeTimeFormatConstructor => &world.relative_time_format_constructor,
|
||||
ProtoKind::RelativeTimeFormatProto => &world.relative_time_format_proto,
|
||||
ProtoKind::DisplayNamesConstructor => &world.display_names_constructor,
|
||||
ProtoKind::DisplayNamesProto => &world.display_names_proto,
|
||||
ProtoKind::SegmenterConstructor => &world.segmenter_constructor,
|
||||
ProtoKind::SegmenterProto => &world.segmenter_proto,
|
||||
ProtoKind::LocaleConstructor => &world.locale_constructor,
|
||||
ProtoKind::LocaleProto => &world.locale_proto,
|
||||
ProtoKind::DurationFormatConstructor => &world.duration_format_constructor,
|
||||
ProtoKind::DurationFormatProto => &world.duration_format_proto,
|
||||
};
|
||||
P::as_ptr(p) as *mut JsObject
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user