use std::ffi::c_void;
use std::ptr;
use std::ptr::NonNull;
#[cfg(test)]
use mozjs::context::JSContext;
use mozjs::conversions::jsstr_to_string;
use mozjs::glue::{CreateJSExternalStringCallbacks, JSExternalStringCallbacksTraps};
use mozjs::jsapi::OnNewGlobalHookOption;
use mozjs::realm::AutoRealm;
use mozjs::rooted;
use mozjs::rust::wrappers2::{
JS_NewExternalStringLatin1, JS_NewExternalUCString, JS_NewGlobalObject,
};
use mozjs::rust::{JSEngine, RealmOptions, Runtime, SIMPLE_GLOBAL_CLASS};
#[test]
fn external_string() {
let engine = JSEngine::init().unwrap();
let mut runtime = Runtime::new(engine.handle());
let context = runtime.cx();
#[cfg(feature = "debugmozjs")]
unsafe {
mozjs::jsapi::SetGCZeal(context.raw_cx(), 2, 1);
}
let h_option = OnNewGlobalHookOption::FireOnNewGlobalHook;
let c_option = RealmOptions::default();
unsafe {
rooted!(&in(context) let global = JS_NewGlobalObject(
context,
&SIMPLE_GLOBAL_CLASS,
ptr::null_mut(),
h_option,
&*c_option,
));
let mut realm = AutoRealm::new_from_handle(context, global.handle());
let context = &mut *realm;
test_latin1_string(context, "test latin1");
test_latin1_string(context, "abcdefghijklmnop"); test_latin1_string(context, "abcdefghijklmnopq"); test_latin1_string(context, "abcdefghijklmno"); test_latin1_string(context, "abcdefghijklmnopqrstuvwxyzabcdef"); test_latin1_string(context, "abcdefghijklmnopqrstuvwxyzabcde"); test_latin1_string(context, "abcdefghijklmnopqrstuvwxyzabcdefg"); test_latin1_string(context, " ! \" # $ % & ' ( ) * + , - . /");
test_latin1_string(context, "0 1 2 3 4 5 6 7 8 9 : ; < = > ?");
test_latin1_string(context, "@ A B C D E F G H I J K L M N O");
test_latin1_string(context, "P Q R S T U V W X Y Z [ \\ ] ^ _");
test_latin1_string(context, "` a b c d e f g h i j k l m n o");
test_latin1_string(context, "p q r s t u v w x y z { | } ~");
test_latin1_string_bytes(
context,
b"\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF",
);
test_latin1_string_bytes(
context,
b"\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF",
);
test_latin1_string_bytes(
context,
b"\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF",
);
test_latin1_string_bytes(
context,
b"\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF",
);
test_latin1_string_bytes(
context,
b"\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF",
);
test_latin1_string_bytes(
context,
b"\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF",
);
let utf16_base = "test utf-16 $€ \u{10437}\u{24B62}";
let utf16_boxed = utf16_base
.encode_utf16()
.collect::<Vec<_>>()
.into_boxed_slice();
let utf16_len = utf16_boxed.len();
let utf16_chars = Box::into_raw(utf16_boxed).cast::<u16>();
let callbacks = CreateJSExternalStringCallbacks(
&EXTERNAL_STRING_CALLBACKS_TRAPS,
utf16_len as *mut c_void,
);
rooted!(&in(context) let utf16_jsstr = JS_NewExternalUCString(
context,
utf16_chars,
utf16_len,
callbacks
));
assert_eq!(
jsstr_to_string(context.raw_cx(), NonNull::new(utf16_jsstr.get()).unwrap()),
utf16_base
);
}
}
#[cfg(test)]
unsafe fn test_latin1_string(context: &mut JSContext, latin1_base: &str) {
let latin1_boxed = latin1_base.as_bytes().to_vec().into_boxed_slice();
let latin1_chars = Box::into_raw(latin1_boxed).cast::<u8>();
let callbacks = CreateJSExternalStringCallbacks(
&EXTERNAL_STRING_CALLBACKS_TRAPS,
latin1_base.len() as *mut c_void,
);
rooted!(&in(context) let latin1_jsstr = JS_NewExternalStringLatin1(
context,
latin1_chars,
latin1_base.len(),
callbacks
));
assert_eq!(
jsstr_to_string(context.raw_cx(), NonNull::new(latin1_jsstr.get()).unwrap()),
latin1_base
);
}
#[cfg(test)]
unsafe fn test_latin1_string_bytes(context: &mut JSContext, latin1_base: &[u8]) {
let latin1_boxed = latin1_base.to_vec().into_boxed_slice();
let latin1_chars = Box::into_raw(latin1_boxed).cast::<u8>();
let callbacks = CreateJSExternalStringCallbacks(
&EXTERNAL_STRING_CALLBACKS_TRAPS,
latin1_base.len() as *mut c_void,
);
rooted!(&in(context) let latin1_jsstr = JS_NewExternalStringLatin1(
context,
latin1_chars,
latin1_base.len(),
callbacks
));
assert_eq!(
jsstr_to_string(context.raw_cx(), NonNull::new(latin1_jsstr.get()).unwrap()),
encoding_rs::mem::decode_latin1(latin1_base)
);
}
static EXTERNAL_STRING_CALLBACKS_TRAPS: JSExternalStringCallbacksTraps =
JSExternalStringCallbacksTraps {
latin1Finalize: Some(latin1::finalize),
latin1SizeOfBuffer: Some(latin1::size_of),
utf16Finalize: Some(utf16::finalize),
utf16SizeOfBuffer: Some(utf16::size_of),
};
mod latin1 {
use std::ffi::c_void;
use std::slice;
use mozjs::jsapi::mozilla::MallocSizeOf;
pub unsafe extern "C" fn finalize(data: *const c_void, chars: *mut u8) {
let slice = slice::from_raw_parts_mut(chars, data as usize);
let _ = Box::from_raw(slice);
}
pub unsafe extern "C" fn size_of(data: *const c_void, _: *const u8, _: MallocSizeOf) -> usize {
data as usize
}
}
mod utf16 {
use std::ffi::c_void;
use std::slice;
use mozjs::jsapi::mozilla::MallocSizeOf;
pub unsafe extern "C" fn finalize(data: *const c_void, chars: *mut u16) {
let slice = slice::from_raw_parts_mut(chars, data as usize);
let _ = Box::from_raw(slice);
}
pub unsafe extern "C" fn size_of(data: *const c_void, _: *const u16, _: MallocSizeOf) -> usize {
data as usize
}
}