Skip to content

Commit

Permalink
Move all the rendering code to the builtin function struct
Browse files Browse the repository at this point in the history
  • Loading branch information
kvnvelasco committed Jun 14, 2021
1 parent ba2ba44 commit aff5716
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 65 deletions.
75 changes: 74 additions & 1 deletion boa/src/builtins/function/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use crate::{
BoaProfiler, Context, Result, Value,
};
use bitflags::bitflags;
use std::fmt::{self, Debug};
use std::fmt::{self, Debug, Display};

#[cfg(test)]
mod tests;
Expand Down Expand Up @@ -329,6 +329,78 @@ impl BuiltInFunctionObject {
// TODO?: 5. PrepareForTailCall
context.call(this, &this_arg, &arg_list)
}

fn to_string(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let name = {
// Is there a case here where if there is no name field on a value
// We it should default to None? Do all functions have names set?
let value = this.get_field("name", &mut *context)?;
if value.is_null_or_undefined() {
None
} else {
Some(value.to_string(context)?)
}
};

let function = {
let object = this.clone().as_object().expect("Should be an object");

let func = object
.borrow()
.as_function()
.map(|f| f.clone())
.expect("Should be a function");

func
};

match (&function, name) {
(Function::BuiltIn(_, _), Some(name)) => {
Ok(format!("function {}() {{\n [native Code]\n}}", &name).into())
}
(Function::Ordinary { body, params, .. }, Some(name)) => {
let arguments: String = params
.iter()
.map(|param| param.name())
.collect::<Vec<&str>>()
.join(", ");

let statement_list = &*body;
// This is a kluge. The implementaion in browser seems to suggest that
// the value here is printed exactly as defined in source. I'm not sure if
// that's possible here, but for now here's a dumb heuristic that prints functions
let is_multiline = {
let value = statement_list.to_string();
value.lines().count() > 1
};
if is_multiline {
Ok(
// ?? For some reason statement_list string implementation
// sticks a \n at the end no matter what
format!(
"{}({}) {{\n{}}}",
&name,
arguments,
statement_list.to_string()
)
.into(),
)
} else {
Ok(format!(
"{}({}) {{{}}}",
&name,
arguments,
// The trim here is to remove a \n stuck at the end
// of the statement_list to_string method
statement_list.to_string().trim()
)
.into())
}
}

_ => Ok("TODO".into()),
}
}
}

impl BuiltIn for BuiltInFunctionObject {
Expand Down Expand Up @@ -358,6 +430,7 @@ impl BuiltIn for BuiltInFunctionObject {
.length(Self::LENGTH)
.method(Self::call, "call", 1)
.method(Self::apply, "apply", 1)
.method(Self::to_string, "toString", 0)
.build();

(Self::NAME, function_object.into(), Self::attribute())
Expand Down
67 changes: 3 additions & 64 deletions boa/src/value/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -612,70 +612,9 @@ impl Value {
Value::String(string) => Ok(string.clone()),
Value::Symbol(_) => Err(context.construct_type_error("can't convert symbol to string")),
Value::BigInt(ref bigint) => Ok(bigint.to_string().into()),
Value::Object(gc_object) => {
if let Some(function) = gc_object.borrow().as_function() {
use crate::builtins::function::Function;
let name = {
// Is there a case here where if there is no name field on a value
// We it should default to None? Do all functions have names set?
let value = self.get_field("name", &mut *context)?;
if value.is_null_or_undefined() {
None
} else {
Some(value.to_string(context)?)
}
};

match (function, name) {
(Function::BuiltIn(_, _), Some(name)) => {
Ok(format!("function {}() {{\n [native Code]\n}}", &name).into())
}
(Function::Ordinary { body, params, .. }, Some(name)) => {
let arguments: String = params
.iter()
.map(|param| param.name())
.collect::<Vec<&str>>()
.join(", ");

let statement_list = &**body;
// This is a kluge. The implementaion in browser seems to suggest that
// the value here is printed exactly as defined in source. I'm not sure if
// that's possible here, but for now here's a dumb heuristic that prints functions
let is_multiline = {
let value = statement_list.to_string();
value.lines().count() > 1
};
if is_multiline {
Ok(
// ?? For some reason statement_list string implementation
// sticks a \n at the end no matter what
format!(
"{}({}) {{\n{}}}",
&name,
arguments,
statement_list.to_string()
)
.into(),
)
} else {
Ok(format!(
"{}({}) {{{}}}",
&name,
arguments,
// The trim here is to remove a \n stuck at the end
// of the statement_list to_string method
statement_list.to_string().trim()
)
.into())
}
}

_ => Ok("TODO".into()),
}
} else {
let primitive = self.to_primitive(context, PreferredType::String)?;
primitive.to_string(context)
}
Value::Object(_) => {
let primitive = self.to_primitive(context, PreferredType::String)?;
primitive.to_string(context)
}
}
}
Expand Down

0 comments on commit aff5716

Please sign in to comment.