|
|
|
@ -14,12 +14,13 @@
|
|
|
|
|
#[cfg(test)] |
|
|
|
|
mod tests; |
|
|
|
|
|
|
|
|
|
use boa_engine::property::Attribute; |
|
|
|
|
use boa_engine::{ |
|
|
|
|
js_str, js_string, |
|
|
|
|
native_function::NativeFunction, |
|
|
|
|
object::{JsObject, ObjectInitializer}, |
|
|
|
|
value::{JsValue, Numeric}, |
|
|
|
|
Context, JsArgs, JsData, JsError, JsResult, JsStr, JsString, |
|
|
|
|
Context, JsArgs, JsData, JsError, JsResult, JsStr, JsString, JsSymbol, |
|
|
|
|
}; |
|
|
|
|
use boa_gc::{Finalize, Trace}; |
|
|
|
|
use rustc_hash::FxHashMap; |
|
|
|
@ -86,9 +87,19 @@ impl Logger for DefaultLogger {
|
|
|
|
|
|
|
|
|
|
/// This represents the `console` formatter.
|
|
|
|
|
fn formatter(data: &[JsValue], context: &mut Context) -> JsResult<String> { |
|
|
|
|
fn to_string(value: &JsValue, context: &mut Context) -> JsResult<String> { |
|
|
|
|
// There is a slight difference between the standard [`JsValue::to_string`] and
|
|
|
|
|
// the way Console actually logs, w.r.t Symbols.
|
|
|
|
|
if let Some(s) = value.as_symbol() { |
|
|
|
|
Ok(s.to_string()) |
|
|
|
|
} else { |
|
|
|
|
Ok(value.to_string(context)?.to_std_string_escaped()) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
match data { |
|
|
|
|
[] => Ok(String::new()), |
|
|
|
|
[val] => Ok(val.to_string(context)?.to_std_string_escaped()), |
|
|
|
|
[val] => to_string(val, context), |
|
|
|
|
data => { |
|
|
|
|
let mut formatted = String::new(); |
|
|
|
|
let mut arg_index = 1; |
|
|
|
@ -124,11 +135,27 @@ fn formatter(data: &[JsValue], context: &mut Context) -> JsResult<String> {
|
|
|
|
|
} |
|
|
|
|
/* string */ |
|
|
|
|
's' => { |
|
|
|
|
let arg = data |
|
|
|
|
.get_or_undefined(arg_index) |
|
|
|
|
.to_string(context)? |
|
|
|
|
.to_std_string_escaped(); |
|
|
|
|
formatted.push_str(&arg); |
|
|
|
|
let arg = data.get_or_undefined(arg_index); |
|
|
|
|
|
|
|
|
|
// If a JS value implements `toString()`, call it.
|
|
|
|
|
let mut written = false; |
|
|
|
|
if let Some(obj) = arg.as_object() { |
|
|
|
|
if let Ok(to_string) = obj.get(js_str!("toString"), context) { |
|
|
|
|
if let Some(to_string_fn) = to_string.as_function() { |
|
|
|
|
let arg = to_string_fn |
|
|
|
|
.call(arg, &[], context)? |
|
|
|
|
.to_string(context)?; |
|
|
|
|
formatted.push_str(&arg.to_std_string_escaped()); |
|
|
|
|
written = true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if !written { |
|
|
|
|
let arg = arg.to_string(context)?.to_std_string_escaped(); |
|
|
|
|
formatted.push_str(&arg); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
arg_index += 1; |
|
|
|
|
} |
|
|
|
|
'%' => formatted.push('%'), |
|
|
|
@ -145,10 +172,8 @@ fn formatter(data: &[JsValue], context: &mut Context) -> JsResult<String> {
|
|
|
|
|
|
|
|
|
|
/* unformatted data */ |
|
|
|
|
for rest in data.iter().skip(arg_index) { |
|
|
|
|
formatted.push_str(&format!( |
|
|
|
|
" {}", |
|
|
|
|
rest.to_string(context)?.to_std_string_escaped() |
|
|
|
|
)); |
|
|
|
|
formatted.push(' '); |
|
|
|
|
formatted.push_str(&to_string(rest, context)?); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Ok(formatted) |
|
|
|
@ -175,6 +200,24 @@ impl Console {
|
|
|
|
|
/// Name of the built-in `console` property.
|
|
|
|
|
pub const NAME: JsStr<'static> = js_str!("console"); |
|
|
|
|
|
|
|
|
|
/// Modify the context to include the `console` object.
|
|
|
|
|
///
|
|
|
|
|
/// # Errors
|
|
|
|
|
/// This function will return an error if the property cannot be defined on the global object.
|
|
|
|
|
pub fn register_with_logger<L>(context: &mut Context, logger: L) -> JsResult<()> |
|
|
|
|
where |
|
|
|
|
L: Logger + 'static, |
|
|
|
|
{ |
|
|
|
|
let console = Self::init_with_logger(context, logger); |
|
|
|
|
context.register_global_property( |
|
|
|
|
Self::NAME, |
|
|
|
|
console, |
|
|
|
|
Attribute::WRITABLE | Attribute::CONFIGURABLE, |
|
|
|
|
)?; |
|
|
|
|
|
|
|
|
|
Ok(()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Initializes the `console` with a special logger.
|
|
|
|
|
#[allow(clippy::too_many_lines)] |
|
|
|
|
pub fn init_with_logger<L>(context: &mut Context, logger: L) -> JsObject |
|
|
|
@ -210,98 +253,107 @@ impl Console {
|
|
|
|
|
let state = Rc::new(RefCell::new(Self::default())); |
|
|
|
|
let logger = Rc::new(logger); |
|
|
|
|
|
|
|
|
|
ObjectInitializer::with_native_data(Self::default(), context) |
|
|
|
|
.function( |
|
|
|
|
console_method(Self::assert, state.clone(), logger.clone()), |
|
|
|
|
js_string!("assert"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method_mut(Self::clear, state.clone(), logger.clone()), |
|
|
|
|
js_string!("clear"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method(Self::debug, state.clone(), logger.clone()), |
|
|
|
|
js_string!("debug"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method(Self::error, state.clone(), logger.clone()), |
|
|
|
|
js_string!("error"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method(Self::info, state.clone(), logger.clone()), |
|
|
|
|
js_string!("info"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method(Self::log, state.clone(), logger.clone()), |
|
|
|
|
js_string!("log"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method(Self::trace, state.clone(), logger.clone()), |
|
|
|
|
js_string!("trace"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method(Self::warn, state.clone(), logger.clone()), |
|
|
|
|
js_string!("warn"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method_mut(Self::count, state.clone(), logger.clone()), |
|
|
|
|
js_string!("count"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method_mut(Self::count_reset, state.clone(), logger.clone()), |
|
|
|
|
js_string!("countReset"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method_mut(Self::group, state.clone(), logger.clone()), |
|
|
|
|
js_string!("group"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method_mut(Self::group_collapsed, state.clone(), logger.clone()), |
|
|
|
|
js_string!("groupCollapsed"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method_mut(Self::group_end, state.clone(), logger.clone()), |
|
|
|
|
js_string!("groupEnd"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method_mut(Self::time, state.clone(), logger.clone()), |
|
|
|
|
js_string!("time"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method(Self::time_log, state.clone(), logger.clone()), |
|
|
|
|
js_string!("timeLog"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method_mut(Self::time_end, state.clone(), logger.clone()), |
|
|
|
|
js_string!("timeEnd"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method(Self::dir, state.clone(), logger.clone()), |
|
|
|
|
js_string!("dir"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method(Self::dir, state, logger.clone()), |
|
|
|
|
js_string!("dirxml"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.build() |
|
|
|
|
ObjectInitializer::with_native_data_and_proto( |
|
|
|
|
Self::default(), |
|
|
|
|
JsObject::with_object_proto(context.realm().intrinsics()), |
|
|
|
|
context, |
|
|
|
|
) |
|
|
|
|
.property( |
|
|
|
|
JsSymbol::to_string_tag(), |
|
|
|
|
Self::NAME, |
|
|
|
|
Attribute::CONFIGURABLE, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method(Self::assert, state.clone(), logger.clone()), |
|
|
|
|
js_string!("assert"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method_mut(Self::clear, state.clone(), logger.clone()), |
|
|
|
|
js_string!("clear"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method(Self::debug, state.clone(), logger.clone()), |
|
|
|
|
js_string!("debug"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method(Self::error, state.clone(), logger.clone()), |
|
|
|
|
js_string!("error"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method(Self::info, state.clone(), logger.clone()), |
|
|
|
|
js_string!("info"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method(Self::log, state.clone(), logger.clone()), |
|
|
|
|
js_string!("log"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method(Self::trace, state.clone(), logger.clone()), |
|
|
|
|
js_string!("trace"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method(Self::warn, state.clone(), logger.clone()), |
|
|
|
|
js_string!("warn"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method_mut(Self::count, state.clone(), logger.clone()), |
|
|
|
|
js_string!("count"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method_mut(Self::count_reset, state.clone(), logger.clone()), |
|
|
|
|
js_string!("countReset"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method_mut(Self::group, state.clone(), logger.clone()), |
|
|
|
|
js_string!("group"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method_mut(Self::group_collapsed, state.clone(), logger.clone()), |
|
|
|
|
js_string!("groupCollapsed"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method_mut(Self::group_end, state.clone(), logger.clone()), |
|
|
|
|
js_string!("groupEnd"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method_mut(Self::time, state.clone(), logger.clone()), |
|
|
|
|
js_string!("time"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method(Self::time_log, state.clone(), logger.clone()), |
|
|
|
|
js_string!("timeLog"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method_mut(Self::time_end, state.clone(), logger.clone()), |
|
|
|
|
js_string!("timeEnd"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method(Self::dir, state.clone(), logger.clone()), |
|
|
|
|
js_string!("dir"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.function( |
|
|
|
|
console_method(Self::dir, state, logger.clone()), |
|
|
|
|
js_string!("dirxml"), |
|
|
|
|
0, |
|
|
|
|
) |
|
|
|
|
.build() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Initializes the `console` built-in object.
|
|
|
|
|