mirror of
https://github.com/servo/servo.git
synced 2025-07-23 15:23:42 +01:00
This is the final step of #1799, where the majority of the generated code for the JS bindings is now compiled as part of the script_bindings build step. The remaining pieces in script must live there because they refer to concrete DOM types; all code in script_bindings is generic over the [DomTypes](https://doc.servo.org/script/dom/bindings/codegen/DomTypes/trait.DomTypes.html) trait. My testing with incremental builds shows me a 12 second reduction in build times on my 2024 M4 Macbook Pro when modifying code in the script crate after these changes. Before this PR those changes took 20 seconds to rebuild Servo, and now they take 8 seconds. Testing: Existing WPT tests ensure no regressions. Fixes: #1799 --------- Signed-off-by: Josh Matthews <josh@joshmatthews.net>
631 lines
19 KiB
Rust
631 lines
19 KiB
Rust
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
|
|
use std::ffi::CString;
|
|
use std::os::raw::{c_char, c_void};
|
|
use std::ptr::{self, NonNull};
|
|
use std::slice;
|
|
|
|
use js::conversions::ToJSValConvertible;
|
|
use js::gc::Handle;
|
|
use js::glue::{
|
|
CallJitGetterOp, CallJitMethodOp, CallJitSetterOp, JS_GetReservedSlot,
|
|
RUST_FUNCTION_VALUE_TO_JITINFO,
|
|
};
|
|
use js::jsapi::{
|
|
AtomToLinearString, CallArgs, ExceptionStackBehavior, GetLinearStringCharAt,
|
|
GetLinearStringLength, GetNonCCWObjectGlobal, HandleId as RawHandleId,
|
|
HandleObject as RawHandleObject, Heap, JS_ClearPendingException,
|
|
JS_DeprecatedStringHasLatin1Chars, JS_EnumerateStandardClasses,
|
|
JS_GetLatin1StringCharsAndLength, JS_IsExceptionPending, JS_IsGlobalObject,
|
|
JS_ResolveStandardClass, JSAtom, JSContext, JSJitInfo, JSObject, JSTracer,
|
|
MutableHandleIdVector as RawMutableHandleIdVector, MutableHandleValue as RawMutableHandleValue,
|
|
ObjectOpResult, StringIsArrayIndex,
|
|
};
|
|
use js::jsval::{JSVal, UndefinedValue};
|
|
use js::rust::wrappers::{
|
|
CallOriginalPromiseReject, JS_DeletePropertyById, JS_ForwardGetPropertyTo,
|
|
JS_GetPendingException, JS_GetProperty, JS_GetPrototype, JS_HasProperty, JS_HasPropertyById,
|
|
JS_SetPendingException, JS_SetProperty,
|
|
};
|
|
use js::rust::{
|
|
HandleId, HandleObject, HandleValue, MutableHandleValue, ToString, get_object_class,
|
|
};
|
|
use js::{JS_CALLEE, rooted};
|
|
use malloc_size_of::MallocSizeOfOps;
|
|
|
|
use crate::DomTypes;
|
|
use crate::codegen::Globals::Globals;
|
|
use crate::codegen::InheritTypes::TopTypeId;
|
|
use crate::codegen::PrototypeList::{self, MAX_PROTO_CHAIN_LENGTH, PROTO_OR_IFACE_LENGTH};
|
|
use crate::conversions::{PrototypeCheck, jsstring_to_str, private_from_proto_check};
|
|
use crate::error::throw_invalid_this;
|
|
use crate::interfaces::DomHelpers;
|
|
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
|
|
use crate::str::DOMString;
|
|
use crate::trace::trace_object;
|
|
|
|
/// The struct that holds inheritance information for DOM object reflectors.
|
|
#[derive(Clone, Copy)]
|
|
pub struct DOMClass {
|
|
/// A list of interfaces that this object implements, in order of decreasing
|
|
/// derivedness.
|
|
pub interface_chain: [PrototypeList::ID; MAX_PROTO_CHAIN_LENGTH],
|
|
|
|
/// The last valid index of `interface_chain`.
|
|
pub depth: u8,
|
|
|
|
/// The type ID of that interface.
|
|
pub type_id: TopTypeId,
|
|
|
|
/// The MallocSizeOf function wrapper for that interface.
|
|
pub malloc_size_of: unsafe fn(ops: &mut MallocSizeOfOps, *const c_void) -> usize,
|
|
|
|
/// The `Globals` flag for this global interface, if any.
|
|
pub global: Globals,
|
|
}
|
|
unsafe impl Sync for DOMClass {}
|
|
|
|
/// The JSClass used for DOM object reflectors.
|
|
#[derive(Copy)]
|
|
#[repr(C)]
|
|
pub struct DOMJSClass {
|
|
/// The actual JSClass.
|
|
pub base: js::jsapi::JSClass,
|
|
/// Associated data for DOM object reflectors.
|
|
pub dom_class: DOMClass,
|
|
}
|
|
impl Clone for DOMJSClass {
|
|
fn clone(&self) -> DOMJSClass {
|
|
*self
|
|
}
|
|
}
|
|
unsafe impl Sync for DOMJSClass {}
|
|
|
|
/// The index of the slot where the object holder of that interface's
|
|
/// unforgeable members are defined.
|
|
pub(crate) const DOM_PROTO_UNFORGEABLE_HOLDER_SLOT: u32 = 0;
|
|
|
|
/// The index of the slot that contains a reference to the ProtoOrIfaceArray.
|
|
// All DOM globals must have a slot at DOM_PROTOTYPE_SLOT.
|
|
pub(crate) const DOM_PROTOTYPE_SLOT: u32 = js::JSCLASS_GLOBAL_SLOT_COUNT;
|
|
|
|
/// The flag set on the `JSClass`es for DOM global objects.
|
|
// NOTE: This is baked into the Ion JIT as 0 in codegen for LGetDOMProperty and
|
|
// LSetDOMProperty. Those constants need to be changed accordingly if this value
|
|
// changes.
|
|
pub(crate) const JSCLASS_DOM_GLOBAL: u32 = js::JSCLASS_USERBIT1;
|
|
|
|
/// Returns the ProtoOrIfaceArray for the given global object.
|
|
/// Fails if `global` is not a DOM global object.
|
|
///
|
|
/// # Safety
|
|
/// `global` must point to a valid, non-null JS object.
|
|
pub(crate) unsafe fn get_proto_or_iface_array(global: *mut JSObject) -> *mut ProtoOrIfaceArray {
|
|
assert_ne!(((*get_object_class(global)).flags & JSCLASS_DOM_GLOBAL), 0);
|
|
let mut slot = UndefinedValue();
|
|
JS_GetReservedSlot(global, DOM_PROTOTYPE_SLOT, &mut slot);
|
|
slot.to_private() as *mut ProtoOrIfaceArray
|
|
}
|
|
|
|
/// An array of *mut JSObject of size PROTO_OR_IFACE_LENGTH.
|
|
pub type ProtoOrIfaceArray = [*mut JSObject; PROTO_OR_IFACE_LENGTH];
|
|
|
|
/// Gets the property `id` on `proxy`'s prototype. If it exists, `*found` is
|
|
/// set to true and `*vp` to the value, otherwise `*found` is set to false.
|
|
///
|
|
/// Returns false on JSAPI failure.
|
|
///
|
|
/// # Safety
|
|
/// `cx` must point to a valid, non-null JSContext.
|
|
/// `found` must point to a valid, non-null bool.
|
|
pub(crate) unsafe fn get_property_on_prototype(
|
|
cx: *mut JSContext,
|
|
proxy: HandleObject,
|
|
receiver: HandleValue,
|
|
id: HandleId,
|
|
found: *mut bool,
|
|
vp: MutableHandleValue,
|
|
) -> bool {
|
|
rooted!(in(cx) let mut proto = ptr::null_mut::<JSObject>());
|
|
if !JS_GetPrototype(cx, proxy, proto.handle_mut()) || proto.is_null() {
|
|
*found = false;
|
|
return true;
|
|
}
|
|
let mut has_property = false;
|
|
if !JS_HasPropertyById(cx, proto.handle(), id, &mut has_property) {
|
|
return false;
|
|
}
|
|
*found = has_property;
|
|
if !has_property {
|
|
return true;
|
|
}
|
|
|
|
JS_ForwardGetPropertyTo(cx, proto.handle(), id, receiver, vp)
|
|
}
|
|
|
|
/// Get an array index from the given `jsid`. Returns `None` if the given
|
|
/// `jsid` is not an integer.
|
|
pub fn get_array_index_from_id(id: HandleId) -> Option<u32> {
|
|
let raw_id = *id;
|
|
if raw_id.is_int() {
|
|
return Some(raw_id.to_int() as u32);
|
|
}
|
|
|
|
if raw_id.is_void() || !raw_id.is_string() {
|
|
return None;
|
|
}
|
|
|
|
unsafe {
|
|
let atom = raw_id.to_string() as *mut JSAtom;
|
|
let s = AtomToLinearString(atom);
|
|
if GetLinearStringLength(s) == 0 {
|
|
return None;
|
|
}
|
|
|
|
let chars = [GetLinearStringCharAt(s, 0)];
|
|
let first_char = char::decode_utf16(chars.iter().cloned())
|
|
.next()
|
|
.map_or('\0', |r| r.unwrap_or('\0'));
|
|
if first_char.is_ascii_lowercase() {
|
|
return None;
|
|
}
|
|
|
|
let mut i = 0;
|
|
if StringIsArrayIndex(s, &mut i) {
|
|
Some(i)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
/*let s = jsstr_to_string(cx, RUST_JSID_TO_STRING(raw_id));
|
|
if s.len() == 0 {
|
|
return None;
|
|
}
|
|
|
|
let first = s.chars().next().unwrap();
|
|
if first.is_ascii_lowercase() {
|
|
return None;
|
|
}
|
|
|
|
let mut i: u32 = 0;
|
|
let is_array = if s.is_ascii() {
|
|
let chars = s.as_bytes();
|
|
StringIsArrayIndex1(chars.as_ptr() as *const _, chars.len() as u32, &mut i)
|
|
} else {
|
|
let chars = s.encode_utf16().collect::<Vec<u16>>();
|
|
let slice = chars.as_slice();
|
|
StringIsArrayIndex2(slice.as_ptr(), chars.len() as u32, &mut i)
|
|
};
|
|
|
|
if is_array {
|
|
Some(i)
|
|
} else {
|
|
None
|
|
}*/
|
|
}
|
|
|
|
/// Find the enum equivelent of a string given by `v` in `pairs`.
|
|
/// Returns `Err(())` on JSAPI failure (there is a pending exception), and
|
|
/// `Ok((None, value))` if there was no matching string.
|
|
///
|
|
/// # Safety
|
|
/// `cx` must point to a valid, non-null JSContext.
|
|
#[allow(clippy::result_unit_err)]
|
|
pub(crate) unsafe fn find_enum_value<'a, T>(
|
|
cx: *mut JSContext,
|
|
v: HandleValue,
|
|
pairs: &'a [(&'static str, T)],
|
|
) -> Result<(Option<&'a T>, DOMString), ()> {
|
|
match ptr::NonNull::new(ToString(cx, v)) {
|
|
Some(jsstr) => {
|
|
let search = jsstring_to_str(cx, jsstr);
|
|
Ok((
|
|
pairs
|
|
.iter()
|
|
.find(|&&(key, _)| search == *key)
|
|
.map(|(_, ev)| ev),
|
|
search,
|
|
))
|
|
},
|
|
None => Err(()),
|
|
}
|
|
}
|
|
|
|
/// Get the property with name `property` from `object`.
|
|
/// Returns `Err(())` on JSAPI failure (there is a pending exception), and
|
|
/// `Ok(false)` if there was no property with the given name.
|
|
///
|
|
/// # Safety
|
|
/// `cx` must point to a valid, non-null JSContext.
|
|
#[allow(clippy::result_unit_err)]
|
|
pub unsafe fn get_dictionary_property(
|
|
cx: *mut JSContext,
|
|
object: HandleObject,
|
|
property: &str,
|
|
rval: MutableHandleValue,
|
|
_can_gc: CanGc,
|
|
) -> Result<bool, ()> {
|
|
unsafe fn has_property(
|
|
cx: *mut JSContext,
|
|
object: HandleObject,
|
|
property: &CString,
|
|
found: &mut bool,
|
|
) -> bool {
|
|
JS_HasProperty(cx, object, property.as_ptr(), found)
|
|
}
|
|
unsafe fn get_property(
|
|
cx: *mut JSContext,
|
|
object: HandleObject,
|
|
property: &CString,
|
|
value: MutableHandleValue,
|
|
) -> bool {
|
|
JS_GetProperty(cx, object, property.as_ptr(), value)
|
|
}
|
|
|
|
let property = CString::new(property).unwrap();
|
|
if object.get().is_null() {
|
|
return Ok(false);
|
|
}
|
|
|
|
let mut found = false;
|
|
if !has_property(cx, object, &property, &mut found) {
|
|
return Err(());
|
|
}
|
|
|
|
if !found {
|
|
return Ok(false);
|
|
}
|
|
|
|
if !get_property(cx, object, &property, rval) {
|
|
return Err(());
|
|
}
|
|
|
|
Ok(true)
|
|
}
|
|
|
|
/// Set the property with name `property` from `object`.
|
|
/// Returns `Err(())` on JSAPI failure, or null object,
|
|
/// and Ok(()) otherwise
|
|
///
|
|
/// # Safety
|
|
/// `cx` must point to a valid, non-null JSContext.
|
|
#[allow(clippy::result_unit_err)]
|
|
pub(crate) unsafe fn set_dictionary_property(
|
|
cx: *mut JSContext,
|
|
object: HandleObject,
|
|
property: &str,
|
|
value: HandleValue,
|
|
) -> Result<(), ()> {
|
|
if object.get().is_null() {
|
|
return Err(());
|
|
}
|
|
|
|
let property = CString::new(property).unwrap();
|
|
if !JS_SetProperty(cx, object, property.as_ptr(), value) {
|
|
return Err(());
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Returns whether `proxy` has a property `id` on its prototype.
|
|
///
|
|
/// # Safety
|
|
/// `cx` must point to a valid, non-null JSContext.
|
|
pub unsafe fn has_property_on_prototype(
|
|
cx: *mut JSContext,
|
|
proxy: HandleObject,
|
|
id: HandleId,
|
|
found: &mut bool,
|
|
) -> bool {
|
|
rooted!(in(cx) let mut proto = ptr::null_mut::<JSObject>());
|
|
if !JS_GetPrototype(cx, proxy, proto.handle_mut()) {
|
|
return false;
|
|
}
|
|
assert!(!proto.is_null());
|
|
JS_HasPropertyById(cx, proto.handle(), id, found)
|
|
}
|
|
|
|
/// Deletes the property `id` from `object`.
|
|
///
|
|
/// # Safety
|
|
/// `cx` must point to a valid, non-null JSContext.
|
|
pub(crate) unsafe fn delete_property_by_id(
|
|
cx: *mut JSContext,
|
|
object: HandleObject,
|
|
id: HandleId,
|
|
bp: *mut ObjectOpResult,
|
|
) -> bool {
|
|
JS_DeletePropertyById(cx, object, id, bp)
|
|
}
|
|
|
|
unsafe fn generic_call<const EXCEPTION_TO_REJECTION: bool>(
|
|
cx: *mut JSContext,
|
|
argc: libc::c_uint,
|
|
vp: *mut JSVal,
|
|
is_lenient: bool,
|
|
call: unsafe extern "C" fn(
|
|
*const JSJitInfo,
|
|
*mut JSContext,
|
|
RawHandleObject,
|
|
*mut libc::c_void,
|
|
u32,
|
|
*mut JSVal,
|
|
) -> bool,
|
|
can_gc: CanGc,
|
|
) -> bool {
|
|
let args = CallArgs::from_vp(vp, argc);
|
|
|
|
let info = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));
|
|
let proto_id = (*info).__bindgen_anon_2.protoID;
|
|
let cx = SafeJSContext::from_ptr(cx);
|
|
|
|
let thisobj = args.thisv();
|
|
if !thisobj.get().is_null_or_undefined() && !thisobj.get().is_object() {
|
|
throw_invalid_this(cx, proto_id);
|
|
return if EXCEPTION_TO_REJECTION {
|
|
exception_to_promise(*cx, args.rval(), can_gc)
|
|
} else {
|
|
false
|
|
};
|
|
}
|
|
|
|
rooted!(in(*cx) let obj = if thisobj.get().is_object() {
|
|
thisobj.get().to_object()
|
|
} else {
|
|
GetNonCCWObjectGlobal(JS_CALLEE(*cx, vp).to_object_or_null())
|
|
});
|
|
let depth = (*info).__bindgen_anon_3.depth as usize;
|
|
let proto_check = PrototypeCheck::Depth { depth, proto_id };
|
|
let this = match private_from_proto_check(obj.get(), *cx, proto_check) {
|
|
Ok(val) => val,
|
|
Err(()) => {
|
|
if is_lenient {
|
|
debug_assert!(!JS_IsExceptionPending(*cx));
|
|
*vp = UndefinedValue();
|
|
return true;
|
|
} else {
|
|
throw_invalid_this(cx, proto_id);
|
|
return if EXCEPTION_TO_REJECTION {
|
|
exception_to_promise(*cx, args.rval(), can_gc)
|
|
} else {
|
|
false
|
|
};
|
|
}
|
|
},
|
|
};
|
|
call(
|
|
info,
|
|
*cx,
|
|
obj.handle().into(),
|
|
this as *mut libc::c_void,
|
|
argc,
|
|
vp,
|
|
)
|
|
}
|
|
|
|
/// Generic method of IDL interface.
|
|
///
|
|
/// # Safety
|
|
/// `cx` must point to a valid, non-null JSContext.
|
|
/// `vp` must point to a VALID, non-null JSVal.
|
|
pub(crate) unsafe extern "C" fn generic_method<const EXCEPTION_TO_REJECTION: bool>(
|
|
cx: *mut JSContext,
|
|
argc: libc::c_uint,
|
|
vp: *mut JSVal,
|
|
) -> bool {
|
|
generic_call::<EXCEPTION_TO_REJECTION>(cx, argc, vp, false, CallJitMethodOp, CanGc::note())
|
|
}
|
|
|
|
/// Generic getter of IDL interface.
|
|
///
|
|
/// # Safety
|
|
/// `cx` must point to a valid, non-null JSContext.
|
|
/// `vp` must point to a VALID, non-null JSVal.
|
|
pub(crate) unsafe extern "C" fn generic_getter<const EXCEPTION_TO_REJECTION: bool>(
|
|
cx: *mut JSContext,
|
|
argc: libc::c_uint,
|
|
vp: *mut JSVal,
|
|
) -> bool {
|
|
generic_call::<EXCEPTION_TO_REJECTION>(cx, argc, vp, false, CallJitGetterOp, CanGc::note())
|
|
}
|
|
|
|
/// Generic lenient getter of IDL interface.
|
|
///
|
|
/// # Safety
|
|
/// `cx` must point to a valid, non-null JSContext.
|
|
/// `vp` must point to a VALID, non-null JSVal.
|
|
pub(crate) unsafe extern "C" fn generic_lenient_getter<const EXCEPTION_TO_REJECTION: bool>(
|
|
cx: *mut JSContext,
|
|
argc: libc::c_uint,
|
|
vp: *mut JSVal,
|
|
) -> bool {
|
|
generic_call::<EXCEPTION_TO_REJECTION>(cx, argc, vp, true, CallJitGetterOp, CanGc::note())
|
|
}
|
|
|
|
unsafe extern "C" fn call_setter(
|
|
info: *const JSJitInfo,
|
|
cx: *mut JSContext,
|
|
handle: RawHandleObject,
|
|
this: *mut libc::c_void,
|
|
argc: u32,
|
|
vp: *mut JSVal,
|
|
) -> bool {
|
|
if !CallJitSetterOp(info, cx, handle, this, argc, vp) {
|
|
return false;
|
|
}
|
|
*vp = UndefinedValue();
|
|
true
|
|
}
|
|
|
|
/// Generic setter of IDL interface.
|
|
///
|
|
/// # Safety
|
|
/// `cx` must point to a valid, non-null JSContext.
|
|
/// `vp` must point to a VALID, non-null JSVal.
|
|
pub(crate) unsafe extern "C" fn generic_setter(
|
|
cx: *mut JSContext,
|
|
argc: libc::c_uint,
|
|
vp: *mut JSVal,
|
|
) -> bool {
|
|
generic_call::<false>(cx, argc, vp, false, call_setter, CanGc::note())
|
|
}
|
|
|
|
/// Generic lenient setter of IDL interface.
|
|
///
|
|
/// # Safety
|
|
/// `cx` must point to a valid, non-null JSContext.
|
|
/// `vp` must point to a VALID, non-null JSVal.
|
|
pub(crate) unsafe extern "C" fn generic_lenient_setter(
|
|
cx: *mut JSContext,
|
|
argc: libc::c_uint,
|
|
vp: *mut JSVal,
|
|
) -> bool {
|
|
generic_call::<false>(cx, argc, vp, true, call_setter, CanGc::note())
|
|
}
|
|
|
|
/// <https://searchfox.org/mozilla-central/rev/7279a1df13a819be254fd4649e07c4ff93e4bd45/dom/bindings/BindingUtils.cpp#3300>
|
|
/// # Safety
|
|
///
|
|
/// `cx` must point to a valid, non-null JSContext.
|
|
/// `vp` must point to a VALID, non-null JSVal.
|
|
pub(crate) unsafe extern "C" fn generic_static_promise_method(
|
|
cx: *mut JSContext,
|
|
argc: libc::c_uint,
|
|
vp: *mut JSVal,
|
|
) -> bool {
|
|
let args = CallArgs::from_vp(vp, argc);
|
|
|
|
let info = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));
|
|
assert!(!info.is_null());
|
|
// TODO: we need safe wrappers for this in mozjs!
|
|
//assert_eq!((*info)._bitfield_1, JSJitInfo_OpType::StaticMethod as u8)
|
|
let static_fn = (*info).__bindgen_anon_1.staticMethod.unwrap();
|
|
if static_fn(cx, argc, vp) {
|
|
return true;
|
|
}
|
|
exception_to_promise(cx, args.rval(), CanGc::note())
|
|
}
|
|
|
|
/// Coverts exception to promise rejection
|
|
///
|
|
/// <https://searchfox.org/mozilla-central/rev/b220e40ff2ee3d10ce68e07d8a8a577d5558e2a2/dom/bindings/BindingUtils.cpp#3315>
|
|
///
|
|
/// # Safety
|
|
/// `cx` must point to a valid, non-null JSContext.
|
|
pub(crate) unsafe fn exception_to_promise(
|
|
cx: *mut JSContext,
|
|
rval: RawMutableHandleValue,
|
|
_can_gc: CanGc,
|
|
) -> bool {
|
|
rooted!(in(cx) let mut exception = UndefinedValue());
|
|
if !JS_GetPendingException(cx, exception.handle_mut()) {
|
|
return false;
|
|
}
|
|
JS_ClearPendingException(cx);
|
|
if let Some(promise) = NonNull::new(CallOriginalPromiseReject(cx, exception.handle())) {
|
|
promise.to_jsval(cx, MutableHandleValue::from_raw(rval));
|
|
true
|
|
} else {
|
|
// We just give up. Put the exception back.
|
|
JS_SetPendingException(cx, exception.handle(), ExceptionStackBehavior::Capture);
|
|
false
|
|
}
|
|
}
|
|
|
|
/// Trace the resources held by reserved slots of a global object
|
|
///
|
|
/// # Safety
|
|
/// `tracer` must point to a valid, non-null JSTracer.
|
|
/// `obj` must point to a valid, non-null JSObject.
|
|
pub(crate) unsafe fn trace_global(tracer: *mut JSTracer, obj: *mut JSObject) {
|
|
let array = get_proto_or_iface_array(obj);
|
|
for proto in (*array).iter() {
|
|
if !proto.is_null() {
|
|
trace_object(
|
|
tracer,
|
|
"prototype",
|
|
&*(proto as *const *mut JSObject as *const Heap<*mut JSObject>),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Generic method for returning libc::c_void from caller
|
|
pub trait AsVoidPtr {
|
|
fn as_void_ptr(&self) -> *const libc::c_void;
|
|
}
|
|
impl<T> AsVoidPtr for T {
|
|
fn as_void_ptr(&self) -> *const libc::c_void {
|
|
self as *const T as *const libc::c_void
|
|
}
|
|
}
|
|
|
|
// Generic method for returning c_char from caller
|
|
pub(crate) trait AsCCharPtrPtr {
|
|
fn as_c_char_ptr(&self) -> *const c_char;
|
|
}
|
|
|
|
impl AsCCharPtrPtr for [u8] {
|
|
fn as_c_char_ptr(&self) -> *const c_char {
|
|
self as *const [u8] as *const c_char
|
|
}
|
|
}
|
|
|
|
/// Enumerate lazy properties of a global object.
|
|
pub(crate) unsafe extern "C" fn enumerate_global<D: DomTypes>(
|
|
cx: *mut JSContext,
|
|
obj: RawHandleObject,
|
|
_props: RawMutableHandleIdVector,
|
|
_enumerable_only: bool,
|
|
) -> bool {
|
|
assert!(JS_IsGlobalObject(obj.get()));
|
|
if !JS_EnumerateStandardClasses(cx, obj) {
|
|
return false;
|
|
}
|
|
for init_fun in <D as DomHelpers<D>>::interface_map().values() {
|
|
init_fun(SafeJSContext::from_ptr(cx), Handle::from_raw(obj));
|
|
}
|
|
true
|
|
}
|
|
|
|
/// Resolve a lazy global property, for interface objects and named constructors.
|
|
pub(crate) unsafe extern "C" fn resolve_global<D: DomTypes>(
|
|
cx: *mut JSContext,
|
|
obj: RawHandleObject,
|
|
id: RawHandleId,
|
|
rval: *mut bool,
|
|
) -> bool {
|
|
assert!(JS_IsGlobalObject(obj.get()));
|
|
if !JS_ResolveStandardClass(cx, obj, id, rval) {
|
|
return false;
|
|
}
|
|
if *rval {
|
|
return true;
|
|
}
|
|
if !id.is_string() {
|
|
*rval = false;
|
|
return true;
|
|
}
|
|
|
|
let string = id.to_string();
|
|
if !JS_DeprecatedStringHasLatin1Chars(string) {
|
|
*rval = false;
|
|
return true;
|
|
}
|
|
let mut length = 0;
|
|
let ptr = JS_GetLatin1StringCharsAndLength(cx, ptr::null(), string, &mut length);
|
|
assert!(!ptr.is_null());
|
|
let bytes = slice::from_raw_parts(ptr, length);
|
|
|
|
if let Some(init_fun) = <D as DomHelpers<D>>::interface_map().get(bytes) {
|
|
init_fun(SafeJSContext::from_ptr(cx), Handle::from_raw(obj));
|
|
*rval = true;
|
|
} else {
|
|
*rval = false;
|
|
}
|
|
true
|
|
}
|