perf(webapi): generate static accessor function names

Move getter/setter name formatting into the derive macro and reuse the
getter name suffix as the property key, avoiding a third stored name.

Keep shared installation and V8 callback mappings. Cover static names
and Unicode property keys in the existing tests.
This commit is contained in:
ldm0
2026-10-22 02:56:07 +08:00
committed by Donough Liu
parent ce7f503f28
commit bd0ddc3f8d
3 changed files with 37 additions and 7 deletions
+17 -1
View File
@@ -582,6 +582,15 @@ fn static_template_accessor(
let Some(name) = webapi_field_name_literal(attrs.name.as_ref()) else {
return Ok(None);
};
let getter_name = LitStr::new(&format!("get {}", name.value()), name.span());
let setter_name = LitStr::new(
&if attrs.setter.is_some() {
format!("set {}", name.value())
} else {
String::new()
},
name.span(),
);
let getter = attrs.getter.as_ref().ok_or_else(|| {
Error::new(
field.span(),
@@ -604,7 +613,8 @@ fn static_template_accessor(
let setter_receiver = receiver.checks_setter();
Ok(Some(quote! {
::moli_webapi_declare::__private::FunctionTemplateAccessor {
name: #name,
getter_name: #getter_name,
setter_name: #setter_name,
getter: #getter,
setter: #setter,
enumerable: #enumerable,
@@ -2254,6 +2264,8 @@ mod tests {
struct SampleTemplate {
#[webapi(accessor_property, getter = sample_getter)]
value: (),
#[webapi(accessor_property = "mutable-Δ", getter = sample_getter, setter = sample_setter)]
mutable: (),
#[webapi(accessor_property = "named", getter = sample_getter, setter = sample_setter, data = getter_data, setter_data = setter_data, dont_delete)]
named: (),
}
@@ -2262,6 +2274,10 @@ mod tests {
.expect("template accessors should expand")
.to_string();
assert!(tokens.contains("FunctionTemplateAccessor"));
assert!(tokens.contains("getter_name : \"get value\""));
assert!(tokens.contains("setter_name : \"\""));
assert!(tokens.contains("getter_name : \"get mutable-Δ\""));
assert!(tokens.contains("setter_name : \"set mutable-Δ\""));
assert!(tokens.contains("String :: new (scope , \"get named\")"));
assert!(tokens.contains("String :: new (scope , \"set named\")"));
assert!(tokens.contains("install_function_template_accessor"));
+7 -4
View File
@@ -171,9 +171,12 @@ pub fn install_function_template_methods<'s>(
/// Static metadata for a string-named accessor without callback data.
/// Mappings preserve constant tables while using V8's original call-time path.
/// The derive prefixes getter names with "get "; the suffix is the property key.
#[doc(hidden)]
pub struct FunctionTemplateAccessor {
pub name: &'static str,
pub getter_name: &'static str,
/// Empty when no setter is declared.
pub setter_name: &'static str,
pub getter: fn() -> v8::FunctionCallback,
pub setter: Option<fn() -> v8::FunctionCallback>,
pub enumerable: bool,
@@ -192,7 +195,7 @@ pub fn install_function_template_accessors<'s>(
accessors: &[FunctionTemplateAccessor],
) {
for accessor in accessors {
let key = v8str(scope, accessor.name);
let key = v8str(scope, &accessor.getter_name["get ".len()..]);
let mut attributes = v8::PropertyAttribute::NONE;
if !accessor.enumerable {
attributes = attributes | v8::PropertyAttribute::DONT_ENUM;
@@ -207,14 +210,14 @@ pub fn install_function_template_accessors<'s>(
callback: (accessor.getter)(),
length: 0,
data: None,
class_name: v8::String::new(scope, &format!("get {}", accessor.name)),
class_name: v8::String::new(scope, accessor.getter_name),
receiver_access_check: accessor.getter_receiver_access_check,
};
let setter = accessor.setter.map(|mapping| FunctionTemplateMember {
callback: mapping(),
length: 1,
data: None,
class_name: v8::String::new(scope, &format!("set {}", accessor.name)),
class_name: v8::String::new(scope, accessor.setter_name),
receiver_access_check: accessor.setter_receiver_access_check,
});
install_function_template_accessor(scope, target, key.into(), getter, setter, attributes);
+13 -2
View File
@@ -439,7 +439,7 @@ const DYNAMIC_ATTRIBUTE_NAME: &str = "runtimeAttribute";
struct AttributeMetadataTemplateDeclaration {
#[webapi(accessor_property, getter = sample_receiver_getter)]
implicit_name: (),
#[webapi(accessor_property = "explicit-name", getter = sample_receiver_getter)]
#[webapi(accessor_property = "explicit-name-Δ", getter = sample_receiver_getter)]
explicit: (),
#[webapi(native_data_property, getter = sample_native_holder_getter)]
native_holder: (),
@@ -466,7 +466,7 @@ fn prototype_attribute_metadata_tracks_static_attribute_keys() {
let attributes = AttributeMetadataTemplateDeclaration::PROTOTYPE_ATTRIBUTE_NAMES;
assert_eq!(
attributes,
["implicitName", "explicit-name", "nativeHolder"]
["implicitName", "explicit-name-Δ", "nativeHolder"]
);
for attribute in attributes {
assert!(
@@ -477,6 +477,17 @@ fn prototype_attribute_metadata_tracks_static_attribute_keys() {
.boolean_value(scope)
);
}
assert!(
run_script(
scope,
r#"['implicitName', 'explicit-name-Δ'].every(key => {
const descriptor = Object.getOwnPropertyDescriptor(AttributeMetadata.prototype, key);
return descriptor.get.name === `get ${key}`
&& descriptor.get.length === 0 && descriptor.set === undefined;
})"#
)
.boolean_value(scope)
);
assert!(
run_script(
scope,