Browse Source

Refactor old function with new function object (#255)

Co-authored-by: Iban Eguia <iban.eguia@cern.ch>

Co-authored-by: Jason Williams <jwilliams720@bloomberg.net>
Co-authored-by: Iban Eguia <iban.eguia@cern.ch>
pull/370/head
Jason Williams 5 years ago committed by GitHub
parent
commit
f02babf0bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 66
      .vscode/tasks.json
  2. 43
      boa/benches/exec.rs
  3. 114
      boa/src/builtins/array/mod.rs
  4. 18
      boa/src/builtins/boolean/mod.rs
  5. 73
      boa/src/builtins/console/mod.rs
  6. 11
      boa/src/builtins/error.rs
  7. 384
      boa/src/builtins/function/mod.rs
  8. 5
      boa/src/builtins/json/mod.rs
  9. 63
      boa/src/builtins/math/mod.rs
  10. 73
      boa/src/builtins/mod.rs
  11. 33
      boa/src/builtins/number/mod.rs
  12. 116
      boa/src/builtins/object/internal_methods_trait.rs
  13. 154
      boa/src/builtins/object/mod.rs
  14. 69
      boa/src/builtins/regexp/mod.rs
  15. 87
      boa/src/builtins/string/mod.rs
  16. 3
      boa/src/builtins/string/tests.rs
  17. 32
      boa/src/builtins/symbol/mod.rs
  18. 191
      boa/src/builtins/value/mod.rs
  19. 2
      boa/src/environment/function_environment_record.rs
  20. 3
      boa/src/environment/lexical_environment.rs
  21. 250
      boa/src/exec/mod.rs
  22. 2
      boa/src/lib.rs
  23. 10
      boa/src/realm.rs
  24. 6
      boa/src/syntax/ast/constant.rs
  25. 88
      boa/src/syntax/ast/node.rs
  26. 5
      boa/src/syntax/lexer/tests.rs

66
.vscode/tasks.json vendored

@ -7,17 +7,15 @@
"type": "process",
"label": "Cargo Run",
"command": "cargo",
"args": [
"run",
"./tests/js/test.js"
],
"problemMatcher": [
"$rustc"
],
"args": ["run", "./tests/js/test.js"],
"problemMatcher": ["$rustc"],
"group": {
"kind": "build",
"isDefault": true
},
"options": {
"env": { "RUST_BACKTRACE": "full" }
},
"presentation": {
"clear": true
}
@ -26,19 +24,9 @@
"type": "process",
"label": "Get Tokens",
"command": "cargo",
"args": [
"run",
"--",
"-t=Debug",
"./tests/js/test.js"
],
"problemMatcher": [
"$rustc"
],
"group": {
"kind": "build",
"isDefault": true
},
"args": ["run", "--", "-t=Debug", "./tests/js/test.js"],
"problemMatcher": ["$rustc"],
"group": "build",
"presentation": {
"clear": true
}
@ -47,19 +35,9 @@
"type": "process",
"label": "Get AST",
"command": "cargo",
"args": [
"run",
"--",
"-a=Debug",
"./tests/js/test.js"
],
"problemMatcher": [
"$rustc"
],
"group": {
"kind": "build",
"isDefault": true
},
"args": ["run", "--", "-a=Debug", "./tests/js/test.js"],
"problemMatcher": ["$rustc"],
"group": "build",
"presentation": {
"clear": true
}
@ -68,12 +46,8 @@
"type": "process",
"label": "Cargo Test",
"command": "cargo",
"args": [
"test"
],
"problemMatcher": [
"$rustc"
],
"args": ["test"],
"problemMatcher": ["$rustc"],
"group": {
"kind": "test",
"isDefault": true
@ -86,17 +60,9 @@
"type": "process",
"label": "Cargo Test Build",
"command": "cargo",
"args": [
"test",
"--no-run"
],
"problemMatcher": [
"$rustc"
],
"group": {
"kind": "build",
"isDefault": true
}
"args": ["test", "--no-run"],
"problemMatcher": ["$rustc"],
"group": "build"
}
]
}

43
boa/benches/exec.rs

@ -26,29 +26,30 @@ fn symbol_creation(c: &mut Criterion) {
});
}
static FOR_LOOP: &str = r#"
let a = 10;
let b = "hello";
for (;;) {
a += 5;
// TODO: implement for loops.
// static FOR_LOOP: &str = r#"
// let a = 10;
// let b = "hello";
// for (;;) {
// a += 5;
if a < 50 {
b += "world";
}
// if (a < 50) {
// b += "world";
// }
if (a > 100) {
break;
}
}
let c = a;
let d = b;
"#;
// if (a > 100) {
// break;
// }
// }
// let c = a;
// let d = b;
// "#;
fn for_loop_execution(c: &mut Criterion) {
c.bench_function("For loop (Execution)", move |b| {
b.iter(|| exec(black_box(FOR_LOOP)))
});
}
// fn for_loop_execution(c: &mut Criterion) {
// c.bench_function("For loop (Execution)", move |b| {
// b.iter(|| exec(black_box(FOR_LOOP)))
// });
// }
static FIBONACCI: &str = r#"
let num = 12;
@ -73,7 +74,7 @@ criterion_group!(
execution,
create_realm,
symbol_creation,
for_loop_execution,
// for_loop_execution,
fibonacci
);
criterion_main!(execution);

114
boa/src/builtins/array/mod.rs

@ -14,8 +14,7 @@ mod tests;
use crate::{
builtins::{
function::NativeFunctionData,
object::{Object, ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE},
object::{Object, ObjectInternalMethods, ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE},
property::Property,
value::{from_value, to_value, undefined, ResultValue, Value, ValueData},
},
@ -24,6 +23,7 @@ use crate::{
use gc::Gc;
use std::borrow::Borrow;
use std::cmp::{max, min};
use std::ops::Deref;
/// Creates a new `Array` instance.
pub(crate) fn new_array(interpreter: &Interpreter) -> ResultValue {
@ -97,7 +97,7 @@ pub(crate) fn add_to_array_object(array_ptr: &Value, add_values: &[Value]) -> Re
}
/// Create a new array
pub fn make_array(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
pub fn make_array(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
// Make a new Object which will internally represent the Array (mapping
// between indices and values): this creates an Object with no prototype
@ -157,7 +157,7 @@ pub fn make_array(this: &Value, args: &[Value], ctx: &mut Interpreter) -> Result
///
/// [spec]: https://tc39.es/ecma262/#sec-array.isarray
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
pub fn is_array(_this: &Value, args: &[Value], _interpreter: &mut Interpreter) -> ResultValue {
pub fn is_array(_this: &mut Value, args: &[Value], _interpreter: &mut Interpreter) -> ResultValue {
let value_true = Gc::new(ValueData::Boolean(true));
let value_false = Gc::new(ValueData::Boolean(false));
@ -167,7 +167,7 @@ pub fn is_array(_this: &Value, args: &[Value], _interpreter: &mut Interpreter) -
// 1.
ValueData::Object(ref obj) => {
// 2.
if obj.borrow().kind == ObjectKind::Array {
if (*obj).deref().borrow().kind == ObjectKind::Array {
return Ok(value_true);
}
Ok(value_false)
@ -192,7 +192,7 @@ pub fn is_array(_this: &Value, args: &[Value], _interpreter: &mut Interpreter) -
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.concat
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat
pub fn concat(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn concat(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
if args.is_empty() {
// If concat is called with no arguments, it returns the original array
return Ok(this.clone());
@ -231,7 +231,7 @@ pub fn concat(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.push
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push
pub fn push(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn push(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let new_array = add_to_array_object(this, args)?;
Ok(new_array.get_field_slice("length"))
}
@ -246,7 +246,7 @@ pub fn push(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.pop
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/pop
pub fn pop(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn pop(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let curr_length: i32 =
from_value(this.get_field_slice("length")).expect("Could not convert argument to i32");
if curr_length < 1 {
@ -269,7 +269,7 @@ pub fn pop(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.foreach
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
pub fn for_each(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
pub fn for_each(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
if args.is_empty() {
return Err(to_value(
"Missing argument for Array.prototype.forEach".to_string(),
@ -277,16 +277,16 @@ pub fn for_each(this: &Value, args: &[Value], interpreter: &mut Interpreter) ->
}
let callback_arg = args.get(0).expect("Could not get `callbackFn` argument.");
let this_arg = args.get(1).cloned().unwrap_or_else(undefined);
let mut this_arg = args.get(1).cloned().unwrap_or_else(undefined);
let length: i32 =
from_value(this.get_field_slice("length")).expect("Could not get `length` property.");
for i in 0..length {
let element = this.get_field_slice(&i.to_string());
let arguments = vec![element.clone(), to_value(i), this.clone()];
let arguments = [element, to_value(i), this.clone()];
interpreter.call(callback_arg, &this_arg, arguments)?;
interpreter.call(callback_arg, &mut this_arg, &arguments)?;
}
Ok(Gc::new(ValueData::Undefined))
@ -304,7 +304,7 @@ pub fn for_each(this: &Value, args: &[Value], interpreter: &mut Interpreter) ->
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.join
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join
pub fn join(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn join(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let separator = if args.is_empty() {
String::from(",")
} else {
@ -334,7 +334,7 @@ pub fn join(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toString
pub fn to_string(this: &Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
pub fn to_string(this: &mut Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
let method_name = "join";
let mut arguments = vec![to_value(",")];
// 2.
@ -350,10 +350,10 @@ pub fn to_string(this: &Value, _args: &[Value], _ctx: &mut Interpreter) -> Resul
.get_field_slice("toString");
method = from_value(method).expect("failed to get Object.prototype.toString");
arguments = vec![];
arguments = Vec::new();
}
// 4.
let join_result = _ctx.call(&method, this, arguments);
let join_result = _ctx.call(&method, this, &arguments);
let match_string = match join_result {
Ok(v) => match *v {
ValueData::String(ref s) => (*s).clone(),
@ -376,7 +376,7 @@ pub fn to_string(this: &Value, _args: &[Value], _ctx: &mut Interpreter) -> Resul
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.reverse
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse
#[allow(clippy::else_if_without_else)]
pub fn reverse(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn reverse(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let len: i32 =
from_value(this.get_field_slice("length")).expect("Could not convert argument to i32");
let middle: i32 = len.wrapping_div(2);
@ -415,7 +415,7 @@ pub fn reverse(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.shift
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift
pub fn shift(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn shift(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let len: i32 =
from_value(this.get_field_slice("length")).expect("Could not convert argument to i32");
@ -458,7 +458,7 @@ pub fn shift(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.unshift
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/unshift
pub fn unshift(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn unshift(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let len: i32 =
from_value(this.get_field_slice("length")).expect("Could not convert argument to i32");
let arg_c: i32 = args.len() as i32;
@ -503,14 +503,14 @@ pub fn unshift(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.every
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every
pub fn every(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
pub fn every(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
if args.is_empty() {
return Err(to_value(
"missing callback when calling function Array.prototype.every".to_string(),
));
}
let callback = &args[0];
let this_arg = if args.len() > 1 {
let mut this_arg = if args.len() > 1 {
args[1].clone()
} else {
Gc::new(ValueData::Undefined)
@ -520,8 +520,10 @@ pub fn every(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Res
let mut len = max_len;
while i < len {
let element = this.get_field_slice(&i.to_string());
let arguments = vec![element.clone(), to_value(i), this.clone()];
let result = interpreter.call(callback, &this_arg, arguments)?.is_true();
let arguments = [element, to_value(i), this.clone()];
let result = interpreter
.call(callback, &mut this_arg, &arguments)?
.is_true();
if !result {
return Ok(to_value(false));
}
@ -542,7 +544,7 @@ pub fn every(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Res
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.map
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
pub fn map(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
pub fn map(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
if args.is_empty() {
return Err(to_value(
"missing argument 0 when calling function Array.prototype.map",
@ -550,7 +552,7 @@ pub fn map(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Resul
}
let callback = args.get(0).cloned().unwrap_or_else(undefined);
let this_val = args.get(1).cloned().unwrap_or_else(undefined);
let mut this_val = args.get(1).cloned().unwrap_or_else(undefined);
let length: i32 =
from_value(this.get_field_slice("length")).expect("Could not get `length` property.");
@ -560,11 +562,10 @@ pub fn map(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Resul
let values = (0..length)
.map(|idx| {
let element = this.get_field_slice(&idx.to_string());
let args = vec![element, to_value(idx), new.clone()];
let args = [element, to_value(idx), new.clone()];
interpreter
.call(&callback, &this_val, args)
.call(&callback, &mut this_val, &args)
.unwrap_or_else(|_| undefined())
})
.collect::<Vec<Value>>();
@ -591,7 +592,7 @@ pub fn map(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Resul
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.indexof
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf
pub fn index_of(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn index_of(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
// If no arguments, return -1. Not described in spec, but is what chrome does.
if args.is_empty() {
return Ok(to_value(-1));
@ -646,7 +647,7 @@ pub fn index_of(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValu
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.lastindexof
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/lastIndexOf
pub fn last_index_of(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn last_index_of(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
// If no arguments, return -1. Not described in spec, but is what chrome does.
if args.is_empty() {
return Ok(to_value(-1));
@ -695,14 +696,14 @@ pub fn last_index_of(this: &Value, args: &[Value], _: &mut Interpreter) -> Resul
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.find
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
pub fn find(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
pub fn find(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
if args.is_empty() {
return Err(to_value(
"missing callback when calling function Array.prototype.find".to_string(),
));
}
let callback = &args[0];
let this_arg = if args.len() > 1 {
let mut this_arg = if args.len() > 1 {
args[1].clone()
} else {
Gc::new(ValueData::Undefined)
@ -710,8 +711,8 @@ pub fn find(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Resu
let len: i32 = from_value(this.get_field_slice("length")).unwrap();
for i in 0..len {
let element = this.get_field_slice(&i.to_string());
let arguments = vec![element.clone(), to_value(i), this.clone()];
let result = interpreter.call(callback, &this_arg, arguments)?;
let arguments = [element.clone(), to_value(i), this.clone()];
let result = interpreter.call(callback, &mut this_arg, &arguments)?;
if result.is_true() {
return Ok(element);
}
@ -731,7 +732,7 @@ pub fn find(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Resu
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.findindex
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex
pub fn find_index(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
pub fn find_index(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
if args.is_empty() {
return Err(to_value(
"Missing argument for Array.prototype.findIndex".to_string(),
@ -740,7 +741,7 @@ pub fn find_index(this: &Value, args: &[Value], interpreter: &mut Interpreter) -
let predicate_arg = args.get(0).expect("Could not get `predicate` argument.");
let this_arg = args
let mut this_arg = args
.get(1)
.cloned()
.unwrap_or_else(|| Gc::new(ValueData::Undefined));
@ -750,9 +751,9 @@ pub fn find_index(this: &Value, args: &[Value], interpreter: &mut Interpreter) -
for i in 0..length {
let element = this.get_field_slice(&i.to_string());
let arguments = vec![element.clone(), to_value(i), this.clone()];
let arguments = [element, to_value(i), this.clone()];
let result = interpreter.call(predicate_arg, &this_arg, arguments)?;
let result = interpreter.call(predicate_arg, &mut this_arg, &arguments)?;
if result.is_true() {
return Ok(Gc::new(ValueData::Rational(f64::from(i))));
@ -773,7 +774,7 @@ pub fn find_index(this: &Value, args: &[Value], interpreter: &mut Interpreter) -
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.fill
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill
pub fn fill(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn fill(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let len: i32 = from_value(this.get_field_slice("length")).expect("Could not get argument");
let default_value = undefined();
let value = args.get(0).unwrap_or(&default_value);
@ -812,7 +813,7 @@ pub fn fill(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.includes
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes
pub fn includes_value(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn includes_value(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let search_element = args
.get(0)
.cloned()
@ -846,7 +847,7 @@ pub fn includes_value(this: &Value, args: &[Value], _: &mut Interpreter) -> Resu
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.slice
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice
pub fn slice(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
pub fn slice(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
let new_array = new_array(interpreter)?;
let len: i32 =
from_value(this.get_field_slice("length")).expect("Could not convert argument to i32");
@ -895,7 +896,7 @@ pub fn slice(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Res
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.filter
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
pub fn filter(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
pub fn filter(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
if args.is_empty() {
return Err(to_value(
"missing argument 0 when calling function Array.prototype.filter",
@ -903,7 +904,7 @@ pub fn filter(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Re
}
let callback = args.get(0).cloned().unwrap_or_else(undefined);
let this_val = args.get(1).cloned().unwrap_or_else(undefined);
let mut this_val = args.get(1).cloned().unwrap_or_else(undefined);
let length: i32 =
from_value(this.get_field_slice("length")).expect("Could not get `length` property.");
@ -914,10 +915,10 @@ pub fn filter(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Re
.filter_map(|idx| {
let element = this.get_field_slice(&idx.to_string());
let args = vec![element.clone(), to_value(idx), new.clone()];
let args = [element.clone(), to_value(idx), new.clone()];
let callback_result = interpreter
.call(&callback, &this_val, args)
.call(&callback, &mut this_val, &args)
.unwrap_or_else(|_| undefined());
if callback_result.is_true() {
@ -946,14 +947,14 @@ pub fn filter(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Re
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.some
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some
pub fn some(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
pub fn some(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
if args.is_empty() {
return Err(to_value(
"missing callback when calling function Array.prototype.some".to_string(),
));
}
let callback = &args[0];
let this_arg = if args.len() > 1 {
let mut this_arg = if args.len() > 1 {
args[1].clone()
} else {
Gc::new(ValueData::Undefined)
@ -963,8 +964,10 @@ pub fn some(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Resu
let mut len = max_len;
while i < len {
let element = this.get_field_slice(&i.to_string());
let arguments = vec![element.clone(), to_value(i), this.clone()];
let result = interpreter.call(callback, &this_arg, arguments)?.is_true();
let arguments = [element, to_value(i), this.clone()];
let result = interpreter
.call(callback, &mut this_arg, &arguments)?
.is_true();
if result {
return Ok(to_value(true));
}
@ -977,14 +980,6 @@ pub fn some(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Resu
/// Create a new `Array` object.
pub fn create_constructor(global: &Value) -> Value {
// Create Constructor
let object_prototype = global.get_field_slice("Object").get_field_slice(PROTOTYPE);
let mut array_constructor = Object::create(object_prototype);
array_constructor.kind = ObjectKind::Function;
array_constructor.set_internal_method("construct", make_array);
// Todo: add call function
array_constructor.set_internal_method("call", make_array);
// Create prototype
let array_prototype = ValueData::new_obj(None);
let length = Property::default().value(to_value(0_i32));
@ -1011,10 +1006,9 @@ pub fn create_constructor(global: &Value) -> Value {
make_builtin_fn!(slice, named "slice", with length 2, of array_prototype);
make_builtin_fn!(some, named "some", with length 2, of array_prototype);
let array = to_value(array_constructor);
let array = make_constructor_fn!(make_array, make_array, global, array_prototype);
// Static Methods
make_builtin_fn!(is_array, named "isArray", with length 1, of array);
array.set_field_slice(PROTOTYPE, to_value(array_prototype.clone()));
array_prototype.set_field_slice("constructor", array.clone());
array
}

18
boa/src/builtins/boolean/mod.rs

@ -14,7 +14,6 @@ mod tests;
use crate::{
builtins::{
function::NativeFunctionData,
object::{internal_methods_trait::ObjectInternalMethods, Object, ObjectKind, PROTOTYPE},
value::{to_value, ResultValue, Value, ValueData},
},
@ -23,7 +22,7 @@ use crate::{
use std::{borrow::Borrow, ops::Deref};
/// Create a new boolean object - [[Construct]]
pub fn construct_boolean(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn construct_boolean(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
this.set_kind(ObjectKind::Boolean);
// Get the argument, if any
@ -38,7 +37,7 @@ pub fn construct_boolean(this: &Value, args: &[Value], _: &mut Interpreter) -> R
}
/// Return a boolean literal [[Call]]
pub fn call_boolean(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn call_boolean(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
// Get the argument, if any
match args.get(0) {
Some(ref value) => Ok(to_boolean(value)),
@ -54,7 +53,7 @@ pub fn call_boolean(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultVal
///
/// [spec]: https://tc39.es/ecma262/#sec-boolean-object
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean/toString
pub fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let b = this_boolean_value(this);
Ok(to_value(b.to_string()))
}
@ -67,16 +66,12 @@ pub fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue
///
/// [spec]: https://tc39.es/ecma262/#sec-boolean.prototype.valueof
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean/valueOf
pub fn value_of(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn value_of(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(this_boolean_value(this))
}
/// Create a new `Boolean` object
pub fn create_constructor(global: &Value) -> Value {
let mut boolean = Object::default();
boolean.kind = ObjectKind::Function;
boolean.set_internal_method("construct", construct_boolean);
boolean.set_internal_method("call", call_boolean);
// Create Prototype
// https://tc39.es/ecma262/#sec-properties-of-the-boolean-prototype-object
let boolean_prototype = ValueData::new_obj(Some(global));
@ -84,10 +79,7 @@ pub fn create_constructor(global: &Value) -> Value {
make_builtin_fn!(to_string, named "toString", of boolean_prototype);
make_builtin_fn!(value_of, named "valueOf", of boolean_prototype);
let boolean_value = to_value(boolean);
boolean_prototype.set_field_slice("constructor", to_value(boolean_value.clone()));
boolean_value.set_field_slice(PROTOTYPE, boolean_prototype);
boolean_value
make_constructor_fn!(construct_boolean, call_boolean, global, boolean_prototype)
}
// === Utility Functions ===

73
boa/src/builtins/console/mod.rs

@ -18,7 +18,6 @@ mod tests;
use crate::{
builtins::{
function::NativeFunctionData,
object::InternalState,
value::{display_obj, from_value, to_value, FromValue, ResultValue, Value, ValueData},
},
@ -37,7 +36,7 @@ pub struct ConsoleState {
impl ConsoleState {
fn new() -> Self {
ConsoleState {
Self {
count_map: HashMap::new(),
timer_map: HashMap::new(),
groups: vec![],
@ -149,7 +148,7 @@ pub fn formatter(data: &[Value]) -> String {
///
/// [spec]: https://console.spec.whatwg.org/#assert
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/assert
pub fn assert(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn assert(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let assertion = get_arg_at_index::<bool>(args, 0).unwrap_or_default();
if !assertion {
@ -182,7 +181,7 @@ pub fn assert(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue
///
/// [spec]: https://console.spec.whatwg.org/#clear
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/clear
pub fn clear(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn clear(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_mut(|state: &mut ConsoleState| {
state.groups.clear();
});
@ -200,7 +199,7 @@ pub fn clear(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://console.spec.whatwg.org/#debug
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/debug
pub fn debug(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn debug(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|state| logger(LogMessage::Log(formatter(&args[..])), state));
Ok(Gc::new(ValueData::Undefined))
}
@ -215,7 +214,7 @@ pub fn debug(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://console.spec.whatwg.org/#error
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/error
pub fn error(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn error(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|state| logger(LogMessage::Error(formatter(&args[..])), state));
Ok(Gc::new(ValueData::Undefined))
}
@ -230,7 +229,7 @@ pub fn error(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://console.spec.whatwg.org/#info
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/info
pub fn info(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn info(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|state| logger(LogMessage::Info(formatter(&args[..])), state));
Ok(Gc::new(ValueData::Undefined))
}
@ -245,7 +244,7 @@ pub fn info(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://console.spec.whatwg.org/#log
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/log
pub fn log(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn log(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|state| logger(LogMessage::Log(formatter(&args[..])), state));
Ok(Gc::new(ValueData::Undefined))
}
@ -260,7 +259,7 @@ pub fn log(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://console.spec.whatwg.org/#trace
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/trace
pub fn trace(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn trace(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
if !args.is_empty() {
this.with_internal_state_ref(|state| logger(LogMessage::Log(formatter(&args[..])), state));
@ -286,7 +285,7 @@ pub fn trace(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://console.spec.whatwg.org/#warn
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/warn
pub fn warn(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn warn(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|state| logger(LogMessage::Warn(formatter(&args[..])), state));
Ok(Gc::new(ValueData::Undefined))
}
@ -301,7 +300,7 @@ pub fn warn(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://console.spec.whatwg.org/#count
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/count
pub fn count(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn count(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let label = get_arg_at_index::<String>(args, 0).unwrap_or_else(|| "default".to_string());
this.with_internal_state_mut(|state: &mut ConsoleState| {
@ -325,7 +324,7 @@ pub fn count(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://console.spec.whatwg.org/#countreset
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/countReset
pub fn count_reset(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn count_reset(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let label = get_arg_at_index::<String>(args, 0).unwrap_or_else(|| "default".to_string());
this.with_internal_state_mut(|state: &mut ConsoleState| {
@ -355,7 +354,7 @@ fn system_time_in_ms() -> u128 {
///
/// [spec]: https://console.spec.whatwg.org/#time
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/time
pub fn time(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn time(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let label = get_arg_at_index::<String>(args, 0).unwrap_or_else(|| "default".to_string());
this.with_internal_state_mut(|state: &mut ConsoleState| {
@ -383,7 +382,7 @@ pub fn time(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://console.spec.whatwg.org/#timelog
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/timeLog
pub fn time_log(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn time_log(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let label = get_arg_at_index::<String>(args, 0).unwrap_or_else(|| "default".to_string());
this.with_internal_state_mut(|state: &mut ConsoleState| {
@ -415,7 +414,7 @@ pub fn time_log(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValu
///
/// [spec]: https://console.spec.whatwg.org/#timeend
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/timeEnd
pub fn time_end(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn time_end(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let label = get_arg_at_index::<String>(args, 0).unwrap_or_else(|| "default".to_string());
this.with_internal_state_mut(|state: &mut ConsoleState| {
@ -446,7 +445,7 @@ pub fn time_end(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValu
///
/// [spec]: https://console.spec.whatwg.org/#group
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/group
pub fn group(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn group(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let group_label = formatter(args);
this.with_internal_state_mut(|state: &mut ConsoleState| {
@ -467,7 +466,7 @@ pub fn group(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://console.spec.whatwg.org/#groupend
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/groupEnd
pub fn group_end(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn group_end(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_mut(|state: &mut ConsoleState| {
state.groups.pop();
});
@ -485,7 +484,7 @@ pub fn group_end(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue
///
/// [spec]: https://console.spec.whatwg.org/#dir
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/dir
pub fn dir(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn dir(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_mut(|state: &mut ConsoleState| {
logger(
LogMessage::Info(display_obj(
@ -502,25 +501,25 @@ pub fn dir(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// Create a new `console` object
pub fn create_constructor(global: &Value) -> Value {
let console = ValueData::new_obj(Some(global));
console.set_field_slice("assert", to_value(assert as NativeFunctionData));
console.set_field_slice("clear", to_value(clear as NativeFunctionData));
console.set_field_slice("debug", to_value(debug as NativeFunctionData));
console.set_field_slice("error", to_value(error as NativeFunctionData));
console.set_field_slice("info", to_value(info as NativeFunctionData));
console.set_field_slice("log", to_value(log as NativeFunctionData));
console.set_field_slice("trace", to_value(trace as NativeFunctionData));
console.set_field_slice("warn", to_value(warn as NativeFunctionData));
console.set_field_slice("exception", to_value(error as NativeFunctionData));
console.set_field_slice("count", to_value(count as NativeFunctionData));
console.set_field_slice("countReset", to_value(count_reset as NativeFunctionData));
console.set_field_slice("group", to_value(group as NativeFunctionData));
console.set_field_slice("groupCollapsed", to_value(group as NativeFunctionData));
console.set_field_slice("groupEnd", to_value(group_end as NativeFunctionData));
console.set_field_slice("time", to_value(time as NativeFunctionData));
console.set_field_slice("timeLog", to_value(time_log as NativeFunctionData));
console.set_field_slice("timeEnd", to_value(time_end as NativeFunctionData));
console.set_field_slice("dir", to_value(dir as NativeFunctionData));
console.set_field_slice("dirxml", to_value(dir as NativeFunctionData));
make_builtin_fn!(assert, named "assert", of console);
make_builtin_fn!(clear, named "clear", of console);
make_builtin_fn!(debug, named "debug", of console);
make_builtin_fn!(error, named "error", of console);
make_builtin_fn!(info, named "info", of console);
make_builtin_fn!(log, named "log", of console);
make_builtin_fn!(trace, named "trace", of console);
make_builtin_fn!(warn, named "warn", of console);
make_builtin_fn!(error, named "exception", of console);
make_builtin_fn!(count, named "count", of console);
make_builtin_fn!(count_reset, named "countReset", of console);
make_builtin_fn!(group, named "group", of console);
make_builtin_fn!(group, named "groupCollapsed", of console);
make_builtin_fn!(group_end , named "groupEnd", of console);
make_builtin_fn!(time, named "time", of console);
make_builtin_fn!(time_log, named "timeLog", of console);
make_builtin_fn!(time_end, named "timeEnd", of console);
make_builtin_fn!(dir, named "dir", of console);
make_builtin_fn!(dir, named "dirxml", of console);
console.set_internal_state(ConsoleState::new());
console
}

11
boa/src/builtins/error.rs

@ -12,8 +12,7 @@
use crate::{
builtins::{
function::NativeFunctionData,
object::{ObjectKind, PROTOTYPE},
object::{internal_methods_trait::ObjectInternalMethods, Object, ObjectKind, PROTOTYPE},
value::{to_value, ResultValue, Value, ValueData},
},
exec::Interpreter,
@ -21,7 +20,7 @@ use crate::{
use gc::Gc;
/// Create a new error object.
pub fn make_error(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn make_error(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
if !args.is_empty() {
this.set_field_slice(
"message",
@ -48,7 +47,7 @@ pub fn make_error(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultVa
///
/// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString
pub fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let name = this.get_field_slice("name");
let message = this.get_field_slice("message");
Ok(to_value(format!("{}: {}", name, message)))
@ -60,9 +59,7 @@ pub fn _create(global: &Value) -> Value {
prototype.set_field_slice("message", to_value(""));
prototype.set_field_slice("name", to_value("Error"));
make_builtin_fn!(to_string, named "toString", of prototype);
let error = to_value(make_error as NativeFunctionData);
error.set_field_slice(PROTOTYPE, prototype);
error
make_constructor_fn!(make_error, global, prototype)
}
/// Initialise the global object with the `Error` object.

384
boa/src/builtins/function/mod.rs

@ -1,134 +1,327 @@
//! This module implements the global `Function` object.
//! This module implements the global `Function` object as well as creates Native Functions.
//!
//! `Every JavaScript `function` is actually a `Function` object.
//! Objects wrap `Function`s and expose them via call/construct slots.
//!
//! `The `Function` object is used for matching text with a pattern.
//!
//! More information:
//! - [ECMAScript reference][spec]
//! - [MDN documentation][mdn]
//!
//! [spec]: https://tc39.es/ecma262/#sec-function-object
//! [spec]: https://tc39.es/ecma262/#sec-function-objects
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function
#[cfg(test)]
mod tests;
use crate::{
builtins::{
object::{internal_methods_trait::ObjectInternalMethods, Object},
array,
object::{Object, ObjectInternalMethods, ObjectKind, PROTOTYPE},
property::Property,
value::{to_value, ResultValue, Value, ValueData},
},
exec::Interpreter,
environment::lexical_environment::{new_function_environment, Environment},
exec::Executor,
syntax::ast::node::{FormalParameter, Node},
Interpreter,
};
use gc::{custom_trace, Gc};
use gc::{unsafe_empty_trace, Gc, Trace};
use gc_derive::{Finalize, Trace};
use std::fmt::{self, Debug};
use std::ops::Deref;
/// fn(this, arguments, ctx)
pub type NativeFunctionData = fn(&Value, &[Value], &mut Interpreter) -> ResultValue;
/// _fn(this, arguments, ctx) -> ResultValue_ - The signature of a built-in function
pub type NativeFunctionData = fn(&mut Value, &[Value], &mut Interpreter) -> ResultValue;
/// A Javascript `Function` object instance.
///
/// A member of the Object type that may be invoked as a subroutine
/// Sets the ConstructorKind
#[derive(Debug, Copy, Clone)]
pub enum ConstructorKind {
Base,
Derived,
}
/// Defines how this references are interpreted within the formal parameters and code body of the function.
///
/// In our implementation, Function is extending Object by holding an object field which some extra data
/// Arrow functions don't define a `this` and thus are lexical, `function`s do define a this and thus are NonLexical
#[derive(Trace, Finalize, Debug, Clone)]
pub enum Function {
/// A native javascript function
NativeFunc(NativeFunction),
/// A regular javascript function
RegularFunc(RegularFunction),
pub enum ThisMode {
Lexical,
NonLexical,
}
/// Represents a regular javascript function in memory.
#[derive(Trace, Finalize, Debug, Clone)]
pub struct RegularFunction {
/// The fields associated with the function
pub object: Object,
/// This function's expression
pub node: Node,
/// The argument declarations of the function
pub args: Vec<Node>,
/// FunctionBody is specific to this interpreter, it will either be Rust code or JavaScript code (AST Node)
#[derive(Clone, Finalize)]
pub enum FunctionBody {
BuiltIn(NativeFunctionData),
Ordinary(Node),
}
impl RegularFunction {
/// Make a new regular function
#[allow(clippy::cast_possible_wrap)]
pub fn new(node: Node, f_args: Vec<FormalParameter>) -> Self {
let mut args = vec![];
for i in f_args {
let node = if let Some(init) = &i.init {
init.deref().clone()
} else {
Node::Local(i.name.clone())
};
args.push(node);
impl Debug for FunctionBody {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::BuiltIn(_) => write!(f, "native code"),
Self::Ordinary(node) => write!(f, "{}", node),
}
let mut object = Object::default();
object.properties.insert(
"arguments".to_string(),
Property::default().value(Gc::new(ValueData::Integer(args.len() as i32))),
);
Self { object, node, args }
}
}
/// Represents a native javascript function in memory
#[derive(Finalize, Clone)]
pub struct NativeFunction {
/// The fields associated with the function
pub object: Object,
/// The callable function data
pub data: NativeFunctionData,
/// `Trace` implementation for `FunctionBody`.
///
/// This is indeed safe, but we need to mark this as an empty trace because neither
// `NativeFunctionData` nor Node hold any GC'd objects, but Gc doesn't know that. So we need to
/// signal it manually. `rust-gc` does not have a `Trace` implementation for `fn(_, _, _)`.
///
/// <https://github.com/Manishearth/rust-gc/blob/master/gc/src/trace.rs>
unsafe impl Trace for FunctionBody {
unsafe_empty_trace!();
}
/// Signal what sort of function this is
#[derive(Clone, Debug, Copy, Finalize)]
pub enum FunctionKind {
BuiltIn,
Ordinary,
}
impl NativeFunction {
/// Make a new native function with the given function data
pub fn new(data: NativeFunctionData) -> Self {
let object = Object::default();
Self { object, data }
/// Waiting on <https://github.com/Manishearth/rust-gc/issues/87> until we can derive Copy
unsafe impl Trace for FunctionKind {
unsafe_empty_trace!();
}
/// Boa representation of a Function Object.
///
/// <https://tc39.es/ecma262/#sec-ecmascript-function-objects>
#[derive(Trace, Finalize, Clone)]
pub struct Function {
/// Call/Construct Function body
pub body: FunctionBody,
/// Formal Paramaters
pub params: Vec<FormalParameter>,
/// This Mode
pub this_mode: ThisMode,
/// Function kind
pub kind: FunctionKind,
// Environment, built-in functions don't need Environments
pub environment: Option<Environment>,
}
impl Function {
/// This will create an ordinary function object
///
/// <https://tc39.es/ecma262/#sec-ordinaryfunctioncreate>
pub fn create_ordinary(
parameter_list: Vec<FormalParameter>,
scope: Environment,
body: FunctionBody,
this_mode: ThisMode,
) -> Self {
Self {
body,
environment: Some(scope),
params: parameter_list,
kind: FunctionKind::Ordinary,
this_mode,
}
}
/// This will create a built-in function object
///
/// <https://tc39.es/ecma262/#sec-createbuiltinfunction>
pub fn create_builtin(parameter_list: Vec<FormalParameter>, body: FunctionBody) -> Self {
Self {
body,
params: parameter_list,
this_mode: ThisMode::NonLexical,
kind: FunctionKind::BuiltIn,
environment: None,
}
}
/// This will handle calls for both ordinary and built-in functions
///
/// <https://tc39.es/ecma262/#sec-prepareforordinarycall>
/// <https://tc39.es/ecma262/#sec-ecmascript-function-objects-call-thisargument-argumentslist>
pub fn call(
&self,
this: &mut Value, // represents a pointer to this function object wrapped in a GC (not a `this` JS object)
args_list: &[Value],
interpreter: &mut Interpreter,
this_obj: &mut Value,
) -> ResultValue {
match self.kind {
FunctionKind::BuiltIn => match &self.body {
FunctionBody::BuiltIn(func) => func(this_obj, args_list, interpreter),
FunctionBody::Ordinary(_) => {
panic!("Builtin function should not have Ordinary Function body")
}
},
FunctionKind::Ordinary => {
// Create a new Function environment who's parent is set to the scope of the function declaration (self.environment)
// <https://tc39.es/ecma262/#sec-prepareforordinarycall>
let local_env = new_function_environment(
this.clone(),
this_obj.clone(),
Some(self.environment.as_ref().unwrap().clone()),
);
// Add argument bindings to the function environment
for i in 0..self.params.len() {
let param = self.params.get(i).expect("Could not get param");
// Rest Parameters
if param.is_rest_param {
self.add_rest_param(param, i, args_list, interpreter, &local_env);
break;
}
let value = args_list.get(i).expect("Could not get value");
self.add_arguments_to_environment(param, value.clone(), &local_env);
}
// Add arguments object
let arguments_obj = create_unmapped_arguments_object(args_list);
local_env
.borrow_mut()
.create_mutable_binding("arguments".to_string(), false);
local_env
.borrow_mut()
.initialize_binding("arguments", arguments_obj);
interpreter.realm.environment.push(local_env);
// Call body should be set before reaching here
let result = match &self.body {
FunctionBody::Ordinary(ref body) => interpreter.run(body),
_ => panic!("Ordinary function should not have BuiltIn Function body"),
};
// local_env gets dropped here, its no longer needed
interpreter.realm.environment.pop();
result
}
}
}
pub fn construct(
&self,
this: &mut Value, // represents a pointer to this function object wrapped in a GC (not a `this` JS object)
args_list: &[Value],
interpreter: &mut Interpreter,
this_obj: &mut Value,
) -> ResultValue {
match self.kind {
FunctionKind::BuiltIn => match &self.body {
FunctionBody::BuiltIn(func) => func(this_obj, args_list, interpreter),
FunctionBody::Ordinary(_) => {
panic!("Builtin function should not have Ordinary Function body")
}
},
FunctionKind::Ordinary => {
// Create a new Function environment who's parent is set to the scope of the function declaration (self.environment)
// <https://tc39.es/ecma262/#sec-prepareforordinarycall>
let local_env = new_function_environment(
this.clone(),
this_obj.clone(),
Some(self.environment.as_ref().unwrap().clone()),
);
// Add argument bindings to the function environment
for (i, param) in self.params.iter().enumerate() {
// Rest Parameters
if param.is_rest_param {
self.add_rest_param(param, i, args_list, interpreter, &local_env);
break;
}
let value = args_list.get(i).expect("Could not get value");
self.add_arguments_to_environment(param, value.clone(), &local_env);
}
// Add arguments object
let arguments_obj = create_unmapped_arguments_object(args_list);
local_env
.borrow_mut()
.create_mutable_binding("arguments".to_string(), false);
local_env
.borrow_mut()
.initialize_binding("arguments", arguments_obj);
interpreter.realm.environment.push(local_env);
// Call body should be set before reaching here
let result = match &self.body {
FunctionBody::Ordinary(ref body) => interpreter.run(body),
_ => panic!("Ordinary function should not have BuiltIn Function body"),
};
// local_env gets dropped here, its no longer needed
interpreter.realm.environment.pop();
result
}
}
}
// Adds the final rest parameters to the Environment as an array
fn add_rest_param(
&self,
param: &FormalParameter,
index: usize,
args_list: &[Value],
interpreter: &mut Interpreter,
local_env: &Environment,
) {
// Create array of values
let array = array::new_array(interpreter).unwrap();
array::add_to_array_object(&array, &args_list[index..]).unwrap();
// Create binding
local_env
.borrow_mut()
.create_mutable_binding(param.name.clone(), false);
// Set Binding to value
local_env
.borrow_mut()
.initialize_binding(&param.name, array);
}
// Adds an argument to the environment
fn add_arguments_to_environment(
&self,
param: &FormalParameter,
value: Value,
local_env: &Environment,
) {
// Create binding
local_env
.borrow_mut()
.create_mutable_binding(param.name.clone(), false);
// Set Binding to value
local_env
.borrow_mut()
.initialize_binding(&param.name, value);
}
}
impl Debug for NativeFunction {
impl Debug for Function {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{{")?;
for (key, val) in self.object.properties.iter() {
write!(
f,
"{}: {}",
key,
val.value
.as_ref()
.unwrap_or(&Gc::new(ValueData::Undefined))
.clone()
)?;
}
write!(f, "[Not implemented]")?;
write!(f, "}}")
}
}
unsafe impl gc::Trace for NativeFunction {
custom_trace!(this, mark(&this.object));
}
/// Create a new `Function` object
pub fn _create() -> Value {
let function: Object = Object::default();
to_value(function)
}
/// Initialise the global object with the `Function` object
pub fn init(global: &Value) {
let global_ptr = global;
global_ptr.set_field_slice("Function", _create());
/// Function Prototype.
///
/// <https://tc39.es/ecma262/#sec-properties-of-the-function-prototype-object>
pub fn create_function_prototype() {
let mut function_prototype: Object = Object::default();
// Set Kind to function (for historical & compatibility reasons)
// <https://tc39.es/ecma262/#sec-properties-of-the-function-prototype-object>
function_prototype.kind = ObjectKind::Function;
}
/// Arguments
/// https://tc39.es/ecma262/#sec-createunmappedargumentsobject
pub fn create_unmapped_arguments_object(arguments_list: Vec<Value>) -> Value {
/// Arguments.
///
/// <https://tc39.es/ecma262/#sec-createunmappedargumentsobject>
pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value {
let len = arguments_list.len();
let mut obj = Object::default();
obj.set_internal_slot("ParameterMap", Gc::new(ValueData::Undefined));
@ -153,3 +346,16 @@ pub fn create_unmapped_arguments_object(arguments_list: Vec<Value>) -> Value {
to_value(obj)
}
/// Create new function `[[Construct]]`
///
// This gets called when a new Function() is created.
pub fn make_function(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
this.set_kind(ObjectKind::Function);
Ok(this.clone())
}
pub fn create_constructor(global: &Value) -> Value {
let proto = ValueData::new_obj(Some(global));
make_constructor_fn!(make_function, make_function, global, proto)
}

5
boa/src/builtins/json/mod.rs

@ -13,7 +13,6 @@
//! [json]: https://www.json.org/json-en.html
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON
use crate::builtins::function::NativeFunctionData;
use crate::builtins::value::{to_value, ResultValue, Value, ValueData};
use crate::exec::Interpreter;
use serde_json::{self, Value as JSONValue};
@ -34,7 +33,7 @@ mod tests;
/// [spec]: https://tc39.es/ecma262/#sec-json.parse
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse
// TODO: implement optional revever argument.
pub fn parse(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn parse(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
match serde_json::from_str::<JSONValue>(
&args
.get(0)
@ -63,7 +62,7 @@ pub fn parse(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-json.stringify
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
pub fn stringify(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn stringify(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let obj = args.get(0).expect("cannot get argument for JSON.stringify");
let json = obj.to_json().to_string();
Ok(to_value(json))

63
boa/src/builtins/math/mod.rs

@ -12,10 +12,7 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math
use crate::{
builtins::{
function::NativeFunctionData,
value::{from_value, to_value, ResultValue, Value, ValueData},
},
builtins::value::{from_value, to_value, ResultValue, Value, ValueData},
exec::Interpreter,
};
use rand::random;
@ -32,7 +29,7 @@ mod tests;
///
/// [spec]: https://tc39.es/ecma262/#sec-math.abs
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/abs
pub fn abs(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn abs(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
f64::NAN
} else {
@ -50,7 +47,7 @@ pub fn abs(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.acos
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/acos
pub fn acos(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn acos(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
f64::NAN
} else {
@ -68,7 +65,7 @@ pub fn acos(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.acosh
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/acosh
pub fn acosh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn acosh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
f64::NAN
} else {
@ -86,7 +83,7 @@ pub fn acosh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.asin
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/asin
pub fn asin(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn asin(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
f64::NAN
} else {
@ -104,7 +101,7 @@ pub fn asin(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.asinh
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/asinh
pub fn asinh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn asinh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
f64::NAN
} else {
@ -122,7 +119,7 @@ pub fn asinh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.atan
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan
pub fn atan(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn atan(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
f64::NAN
} else {
@ -140,7 +137,7 @@ pub fn atan(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.atanh
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atanh
pub fn atanh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn atanh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
f64::NAN
} else {
@ -158,7 +155,7 @@ pub fn atanh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.atan2
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2
pub fn atan2(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn atan2(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
f64::NAN
} else {
@ -176,7 +173,7 @@ pub fn atan2(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.cbrt
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cbrt
pub fn cbrt(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn cbrt(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
f64::NAN
} else {
@ -194,7 +191,7 @@ pub fn cbrt(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.ceil
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/ceil
pub fn ceil(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn ceil(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
f64::NAN
} else {
@ -212,7 +209,7 @@ pub fn ceil(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.cos
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cos
pub fn cos(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn cos(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
f64::NAN
} else {
@ -230,7 +227,7 @@ pub fn cos(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.cosh
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cosh
pub fn cosh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn cosh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
f64::NAN
} else {
@ -248,7 +245,7 @@ pub fn cosh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.exp
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/exp
pub fn exp(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn exp(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
f64::NAN
} else {
@ -266,7 +263,7 @@ pub fn exp(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.floor
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/floor
pub fn floor(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn floor(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
f64::NAN
} else {
@ -284,7 +281,7 @@ pub fn floor(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.log
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log
pub fn log(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn log(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
f64::NAN
} else {
@ -307,7 +304,7 @@ pub fn log(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.log10
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log10
pub fn log10(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn log10(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
f64::NAN
} else {
@ -330,7 +327,7 @@ pub fn log10(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.log2
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log2
pub fn log2(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn log2(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
f64::NAN
} else {
@ -353,7 +350,7 @@ pub fn log2(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.max
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/max
pub fn max(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn max(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let mut max = f64::NEG_INFINITY;
for arg in args {
let num = arg.to_num();
@ -370,7 +367,7 @@ pub fn max(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.min
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/min
pub fn min(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn min(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let mut max = f64::INFINITY;
for arg in args {
let num = arg.to_num();
@ -387,7 +384,7 @@ pub fn min(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.pow
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/pow
pub fn pow(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn pow(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.len() >= 2 {
let num: f64 = from_value(args.get(0).expect("Could not get argument").clone())
.expect("Could not convert argument to f64");
@ -407,7 +404,7 @@ pub fn pow(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.random
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
pub fn _random(_: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn _random(_: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(random::<f64>()))
}
@ -419,7 +416,7 @@ pub fn _random(_: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.round
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
pub fn round(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn round(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
f64::NAN
} else {
@ -437,7 +434,7 @@ pub fn round(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.sign
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign
pub fn sign(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn sign(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
f64::NAN
} else {
@ -460,7 +457,7 @@ pub fn sign(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.sin
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sin
pub fn sin(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn sin(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
f64::NAN
} else {
@ -478,7 +475,7 @@ pub fn sin(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.sinh
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sinh
pub fn sinh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn sinh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
f64::NAN
} else {
@ -496,7 +493,7 @@ pub fn sinh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.sqrt
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sqrt
pub fn sqrt(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn sqrt(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
f64::NAN
} else {
@ -506,7 +503,7 @@ pub fn sqrt(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
}))
}
/// Get the tangent of a number
pub fn tan(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn tan(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
f64::NAN
} else {
@ -524,7 +521,7 @@ pub fn tan(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.tanh
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/tanh
pub fn tanh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn tanh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
f64::NAN
} else {
@ -542,7 +539,7 @@ pub fn tanh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.trunc
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc
pub fn trunc(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn trunc(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
f64::NAN
} else {

73
boa/src/builtins/mod.rs

@ -5,15 +5,82 @@
/// If no length is provided, the length will be set to 0.
macro_rules! make_builtin_fn {
($fn:ident, named $name:expr, with length $l:tt, of $p:ident) => {
let $fn = to_value($fn as NativeFunctionData);
$fn.set_field_slice("length", to_value($l));
$p.set_field_slice($name, $fn);
let func = crate::builtins::function::Function::create_builtin(
vec![],
crate::builtins::function::FunctionBody::BuiltIn($fn),
);
let mut new_func = crate::builtins::object::Object::function();
new_func.set_call(func);
let new_func_obj = to_value(new_func);
new_func_obj.set_field_slice("length", to_value($l));
$p.set_field_slice($name, new_func_obj);
};
($fn:ident, named $name:expr, of $p:ident) => {
make_builtin_fn!($fn, named $name, with length 0, of $p);
};
}
/// Macro to create a new constructor function
///
/// Either (construct_body, global, prototype)
macro_rules! make_constructor_fn {
($body:ident, $global:ident, $proto:ident) => {{
// Create the native function
let constructor_fn = crate::builtins::function::Function::create_builtin(
vec![],
crate::builtins::function::FunctionBody::BuiltIn($body),
);
// Get reference to Function.prototype
let func_prototype = $global
.get_field_slice("Function")
.get_field_slice(PROTOTYPE);
// Create the function object and point its instance prototype to Function.prototype
let mut constructor_obj = Object::function();
constructor_obj.set_construct(constructor_fn);
constructor_obj.set_internal_slot("__proto__", func_prototype);
let constructor_val = to_value(constructor_obj);
// Set proto.constructor -> constructor_obj
$proto.set_field_slice("constructor", constructor_val.clone());
constructor_val.set_field_slice(PROTOTYPE, $proto);
constructor_val
}};
($construct_body:ident, $call_body:ident, $global:ident, $proto:ident) => {{
// Create the native functions
let construct_fn = crate::builtins::function::Function::create_builtin(
vec![],
crate::builtins::function::FunctionBody::BuiltIn($construct_body),
);
let call_fn = crate::builtins::function::Function::create_builtin(
vec![],
crate::builtins::function::FunctionBody::BuiltIn($call_body),
);
// Get reference to Function.prototype
let func_prototype = $global
.get_field_slice("Function")
.get_field_slice(PROTOTYPE);
// Create the function object and point its instance prototype to Function.prototype
let mut constructor_obj = Object::function();
constructor_obj.set_construct(construct_fn);
constructor_obj.set_call(call_fn);
constructor_obj.set_internal_slot("__proto__", func_prototype);
let constructor_val = to_value(constructor_obj);
// Set proto.constructor -> constructor_obj
$proto.set_field_slice("constructor", constructor_val.clone());
constructor_val.set_field_slice(PROTOTYPE, $proto);
constructor_val
}};
}
pub mod array;
pub mod boolean;
pub mod console;

33
boa/src/builtins/number/mod.rs

@ -18,8 +18,7 @@ mod tests;
use crate::{
builtins::{
function::NativeFunctionData,
object::{internal_methods_trait::ObjectInternalMethods, Object, ObjectKind, PROTOTYPE},
object::{internal_methods_trait::ObjectInternalMethods, Object, PROTOTYPE},
value::{to_value, ResultValue, Value, ValueData},
},
exec::Interpreter,
@ -36,7 +35,7 @@ fn to_number(value: &Value) -> Value {
to_value(0)
}
}
ValueData::Function(_) | ValueData::Symbol(_) | ValueData::Undefined => to_value(f64::NAN),
ValueData::Symbol(_) | ValueData::Undefined => to_value(f64::NAN),
ValueData::Integer(i) => to_value(f64::from(i)),
ValueData::Object(ref o) => (o).deref().borrow().get_internal_slot("NumberData"),
ValueData::Null => to_value(0),
@ -58,7 +57,7 @@ fn num_to_exponential(n: f64) -> String {
}
/// Create a new number `[[Construct]]`
pub fn make_number(this: &Value, args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
pub fn make_number(this: &mut Value, args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
let data = match args.get(0) {
Some(ref value) => to_number(value),
None => to_number(&to_value(0)),
@ -70,7 +69,7 @@ pub fn make_number(this: &Value, args: &[Value], _ctx: &mut Interpreter) -> Resu
/// `Number()` function.
///
/// More Information https://tc39.es/ecma262/#sec-number-constructor-number-value
pub fn call_number(_this: &Value, args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
pub fn call_number(_this: &mut Value, args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
let data = match args.get(0) {
Some(ref value) => to_number(value),
None => to_number(&to_value(0)),
@ -88,7 +87,7 @@ pub fn call_number(_this: &Value, args: &[Value], _ctx: &mut Interpreter) -> Res
///
/// [spec]: https://tc39.es/ecma262/#sec-number.prototype.toexponential
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toExponential
pub fn to_exponential(this: &Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
pub fn to_exponential(this: &mut Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
let this_num = to_number(this).to_num();
let this_str_num = num_to_exponential(this_num);
Ok(to_value(this_str_num))
@ -104,7 +103,7 @@ pub fn to_exponential(this: &Value, _args: &[Value], _ctx: &mut Interpreter) ->
///
/// [spec]: https://tc39.es/ecma262/#sec-number.prototype.tofixed
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed
pub fn to_fixed(this: &Value, args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
pub fn to_fixed(this: &mut Value, args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
let this_num = to_number(this).to_num();
let precision = match args.get(0) {
Some(n) => match n.to_int() {
@ -130,7 +129,7 @@ pub fn to_fixed(this: &Value, args: &[Value], _ctx: &mut Interpreter) -> ResultV
///
/// [spec]: https://tc39.es/ecma262/#sec-number.prototype.tolocalestring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString
pub fn to_locale_string(this: &Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
pub fn to_locale_string(this: &mut Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
let this_num = to_number(this).to_num();
let this_str_num = format!("{}", this_num);
Ok(to_value(this_str_num))
@ -146,7 +145,7 @@ pub fn to_locale_string(this: &Value, _args: &[Value], _ctx: &mut Interpreter) -
///
/// [spec]: https://tc39.es/ecma262/#sec-number.prototype.toexponential
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toPrecision
pub fn to_precision(this: &Value, args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
pub fn to_precision(this: &mut Value, args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
let this_num = to_number(this);
let _num_str_len = format!("{}", this_num.to_num()).len();
let _precision = match args.get(0) {
@ -170,7 +169,7 @@ pub fn to_precision(this: &Value, args: &[Value], _ctx: &mut Interpreter) -> Res
///
/// [spec]: https://tc39.es/ecma262/#sec-number.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toString
pub fn to_string(this: &Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
pub fn to_string(this: &mut Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
Ok(to_value(format!("{}", to_number(this).to_num())))
}
@ -184,20 +183,13 @@ pub fn to_string(this: &Value, _args: &[Value], _ctx: &mut Interpreter) -> Resul
///
/// [spec]: https://tc39.es/ecma262/#sec-number.prototype.valueof
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/valueOf
pub fn value_of(this: &Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
pub fn value_of(this: &mut Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
Ok(to_number(this))
}
/// Create a new `Number` object
pub fn create_constructor(global: &Value) -> Value {
let mut number_constructor = Object::default();
number_constructor.kind = ObjectKind::Function;
number_constructor.set_internal_method("construct", make_number);
number_constructor.set_internal_method("call", call_number);
let number_prototype = ValueData::new_obj(Some(global));
number_prototype.set_internal_slot("NumberData", to_value(0));
make_builtin_fn!(to_exponential, named "toExponential", with length 1, of number_prototype);
@ -207,8 +199,5 @@ pub fn create_constructor(global: &Value) -> Value {
make_builtin_fn!(to_string, named "toString", with length 1, of number_prototype);
make_builtin_fn!(value_of, named "valueOf", of number_prototype);
let number = to_value(number_constructor);
number_prototype.set_field_slice("constructor", number.clone());
number.set_field_slice(PROTOTYPE, number_prototype);
number
make_constructor_fn!(make_number, call_number, global, number_prototype)
}

116
boa/src/builtins/object/internal_methods_trait.rs

@ -8,7 +8,7 @@
use crate::builtins::{
object::{Object, INSTANCE_PROTOTYPE},
property::Property,
value::{to_value, Value, ValueData},
value::{same_value, to_value, Value, ValueData},
};
use gc::Gc;
use std::borrow::Borrow;
@ -39,7 +39,7 @@ pub trait ObjectInternalMethods {
// the parent value variant should be an object
// In the unlikely event it isn't return false
return match *parent {
ValueData::Object(ref obj) => obj.borrow().has_property(val),
ValueData::Object(ref obj) => (*obj).deref().borrow().has_property(val),
_ => false,
};
}
@ -168,6 +168,116 @@ pub trait ObjectInternalMethods {
}
}
fn define_own_property(&mut self, property_key: String, desc: Property) -> bool {
let mut current = self.get_own_property(&to_value(property_key.to_string()));
let extensible = self.is_extensible();
// https://tc39.es/ecma262/#sec-validateandapplypropertydescriptor
// There currently isn't a property, lets create a new one
if current.value.is_none() || current.value.as_ref().expect("failed").is_undefined() {
if !extensible {
return false;
}
self.insert_property(property_key, desc);
return true;
}
// If every field is absent we don't need to set anything
if desc.is_none() {
return true;
}
// 4
if !current.configurable.unwrap_or(false) {
if desc.configurable.is_some() && desc.configurable.expect("unable to get prop desc") {
return false;
}
if desc.enumerable.is_some()
&& (desc.enumerable.as_ref().expect("unable to get prop desc")
!= current
.enumerable
.as_ref()
.expect("unable to get prop desc"))
{
return false;
}
}
// 5
if desc.is_generic_descriptor() {
// 6
} else if current.is_data_descriptor() != desc.is_data_descriptor() {
// a
if !current.configurable.expect("unable to get prop desc") {
return false;
}
// b
if current.is_data_descriptor() {
// Convert to accessor
current.value = None;
current.writable = None;
} else {
// c
// convert to data
current.get = None;
current.set = None;
}
self.insert_property(property_key.clone(), current);
// 7
} else if current.is_data_descriptor() && desc.is_data_descriptor() {
// a
if !current.configurable.expect("unable to get prop desc")
&& !current.writable.expect("unable to get prop desc")
{
if desc.writable.is_some() && desc.writable.expect("unable to get prop desc") {
return false;
}
if desc.value.is_some()
&& !same_value(
&desc.value.clone().unwrap(),
&current.value.clone().unwrap(),
false,
)
{
return false;
}
return true;
}
// 8
} else {
if !current.configurable.unwrap() {
if desc.set.is_some()
&& !same_value(
&desc.set.clone().unwrap(),
&current.set.clone().unwrap(),
false,
)
{
return false;
}
if desc.get.is_some()
&& !same_value(
&desc.get.clone().unwrap(),
&current.get.clone().unwrap(),
false,
)
{
return false;
}
}
return true;
}
// 9
self.insert_property(property_key, desc);
true
}
/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getownproperty-p
/// The specification returns a Property Descriptor or Undefined. These are 2 separate types and we can't do that here.
fn get_own_property(&self, prop: &Value) -> Property;
@ -181,8 +291,6 @@ pub trait ObjectInternalMethods {
self.get_internal_slot(INSTANCE_PROTOTYPE)
}
fn define_own_property(&mut self, property_key: String, desc: Property) -> bool;
/// Utility function to get an immutable internal slot or Null
fn get_internal_slot(&self, name: &str) -> Value;

154
boa/src/builtins/object/mod.rs

@ -15,14 +15,16 @@
use crate::{
builtins::{
function::NativeFunctionData,
function::Function,
property::Property,
value::{from_value, same_value, to_value, ResultValue, Value, ValueData},
},
exec::Interpreter,
};
use gc::Gc;
use gc::{unsafe_empty_trace, Gc, Trace};
use gc_derive::{Finalize, Trace};
use std::fmt::{self, Debug};
use std::fmt::{Display, Error, Formatter};
use std::{borrow::Borrow, collections::HashMap, ops::Deref};
pub use internal_methods_trait::ObjectInternalMethods;
@ -38,7 +40,7 @@ pub static PROTOTYPE: &str = "prototype";
pub static INSTANCE_PROTOTYPE: &str = "__proto__";
/// The internal representation of an JavaScript object.
#[derive(Trace, Finalize, Debug, Clone)]
#[derive(Trace, Finalize, Clone)]
pub struct Object {
/// The type of the object.
pub kind: ObjectKind,
@ -50,6 +52,26 @@ pub struct Object {
pub sym_properties: Box<HashMap<i32, Property>>,
/// Some rust object that stores internal state
pub state: Option<Box<InternalStateCell>>,
/// [[Call]]
pub call: Option<Function>,
/// [[Construct]]
pub construct: Option<Function>,
}
impl Debug for Object {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
writeln!(f, "{{")?;
writeln!(f, "\tkind: {}", self.kind)?;
writeln!(f, "\tstate: {:?}", self.state)?;
writeln!(f, "\tcall: {:?}", self.call)?;
writeln!(f, "\tconstruct: {:?}", self.construct)?;
writeln!(f, "\tproperties: {{")?;
for (key, _) in self.properties.iter() {
writeln!(f, "\t\t{}", key)?;
}
writeln!(f, "\t }}")?;
write!(f, "}}")
}
}
impl ObjectInternalMethods for Object {
@ -146,7 +168,7 @@ impl ObjectInternalMethods for Object {
}
}
ValueData::Symbol(ref sym) => {
let sym_id = sym
let sym_id = (**sym)
.borrow()
.get_internal_slot("SymbolData")
.to_string()
@ -318,6 +340,24 @@ impl Object {
properties: Box::new(HashMap::new()),
sym_properties: Box::new(HashMap::new()),
state: None,
call: None,
construct: None,
};
object.set_internal_slot("extensible", to_value(true));
object
}
/// Return a new ObjectData struct, with `kind` set to Ordinary
pub fn function() -> Self {
let mut object = Self {
kind: ObjectKind::Function,
internal_slots: Box::new(HashMap::new()),
properties: Box::new(HashMap::new()),
sym_properties: Box::new(HashMap::new()),
state: None,
call: None,
construct: None,
};
object.set_internal_slot("extensible", to_value(true));
@ -340,17 +380,14 @@ impl Object {
obj
}
/// Utility function to set an internal slot which is a function.
pub fn set_internal_method(&mut self, name: &str, val: NativeFunctionData) {
self.internal_slots.insert(name.to_string(), to_value(val));
/// Set [[Call]]
pub fn set_call(&mut self, val: Function) {
self.call = Some(val);
}
/// Utility function to set a method on this object.
///
/// The native function will live in the `properties` field of the Object.
pub fn set_method(&mut self, name: &str, val: NativeFunctionData) {
self.properties
.insert(name.to_string(), Property::default().value(to_value(val)));
/// set [[Construct]]
pub fn set_construct(&mut self, val: Function) {
self.construct = Some(val);
}
/// Return a new Boolean object whose `[[BooleanData]]` internal slot is set to argument.
@ -361,6 +398,8 @@ impl Object {
properties: Box::new(HashMap::new()),
sym_properties: Box::new(HashMap::new()),
state: None,
call: None,
construct: None,
};
obj.internal_slots
@ -376,6 +415,8 @@ impl Object {
properties: Box::new(HashMap::new()),
sym_properties: Box::new(HashMap::new()),
state: None,
call: None,
construct: None,
};
obj.internal_slots
@ -391,6 +432,8 @@ impl Object {
properties: Box::new(HashMap::new()),
sym_properties: Box::new(HashMap::new()),
state: None,
call: None,
construct: None,
};
obj.internal_slots
@ -409,14 +452,34 @@ impl Object {
ValueData::Boolean(_) => Ok(Self::from_boolean(value)),
ValueData::Rational(_) => Ok(Self::from_number(value)),
ValueData::String(_) => Ok(Self::from_string(value)),
ValueData::Object(ref obj) => Ok(obj.borrow().clone()),
ValueData::Object(ref obj) => Ok((*obj).deref().borrow().clone()),
_ => Err(()),
}
}
/// It determines if Object is a callable function with a [[Call]] internal method.
///
/// More information:
/// - [EcmaScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-iscallable
pub fn is_callable(&self) -> bool {
self.call.is_some()
}
/// It determines if Object is a function object with a [[Construct]] internal method.
///
/// More information:
/// - [EcmaScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-isconstructor
pub fn is_constructor(&self) -> bool {
self.construct.is_some()
}
}
/// Defines the different types of objects.
#[derive(Trace, Finalize, Clone, Debug, Eq, PartialEq)]
#[derive(Finalize, Debug, Copy, Clone, Eq, PartialEq)]
pub enum ObjectKind {
Function,
Array,
@ -428,19 +491,50 @@ pub enum ObjectKind {
Number,
}
impl Display for ObjectKind {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
write!(
f,
"{}",
match self {
Self::Function => "Function",
Self::Array => "Array",
Self::String => "String",
Self::Symbol => "Symbol",
Self::Error => "Error",
Self::Ordinary => "Ordinary",
Self::Boolean => "Boolean",
Self::Number => "Number",
}
)
}
}
/// `Trace` implementation for `ObjectKind`.
///
/// This is indeed safe, but we need to mark this as an empty trace because neither
// `NativeFunctionData` nor Node hold any GC'd objects, but Gc doesn't know that. So we need to
/// signal it manually. `rust-gc` does not have a `Trace` implementation for `fn(_, _, _)`.
///
/// <https://github.com/Manishearth/rust-gc/blob/master/gc/src/trace.rs>
/// Waiting on <https://github.com/Manishearth/rust-gc/issues/87> until we can derive Copy
unsafe impl Trace for ObjectKind {
unsafe_empty_trace!();
}
/// Create a new object.
pub fn make_object(_: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn make_object(_: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(Gc::new(ValueData::Undefined))
}
/// Get the `prototype` of an object.
pub fn get_proto_of(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn get_proto_of(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let obj = args.get(0).expect("Cannot get object");
Ok(obj.get_field_slice(INSTANCE_PROTOTYPE))
}
/// Set the `prototype` of an object.
pub fn set_proto_of(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn set_proto_of(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let obj = args.get(0).expect("Cannot get object").clone();
let proto = args.get(1).expect("Cannot get object").clone();
obj.set_internal_slot(INSTANCE_PROTOTYPE, proto);
@ -448,7 +542,7 @@ pub fn set_proto_of(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultVal
}
/// Define a property in an object
pub fn define_prop(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn define_prop(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let obj = args.get(0).expect("Cannot get object");
let prop = from_value::<String>(args.get(1).expect("Cannot get object").clone())
.expect("Cannot get object");
@ -468,7 +562,7 @@ pub fn define_prop(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValu
///
/// [spec]: https://tc39.es/ecma262/#sec-object.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString
pub fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(this.to_string()))
}
@ -483,7 +577,7 @@ pub fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue
///
/// [spec]: https://tc39.es/ecma262/#sec-object.prototype.hasownproperty
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty
pub fn has_own_prop(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn has_own_prop(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let prop = if args.is_empty() {
None
} else {
@ -496,14 +590,22 @@ pub fn has_own_prop(this: &Value, args: &[Value], _: &mut Interpreter) -> Result
/// Create a new `Object` object.
pub fn create_constructor(_: &Value) -> Value {
let object = to_value(make_object as NativeFunctionData);
let mut constructor_obj = Object::function();
// Create the native function
let constructor_fn = crate::builtins::function::Function::create_builtin(
vec![],
crate::builtins::function::FunctionBody::BuiltIn(make_object),
);
constructor_obj.set_construct(constructor_fn);
let object = to_value(constructor_obj);
// Prototype chain ends here VV
let mut prototype = Object::default();
prototype.set_method("hasOwnProperty", has_own_prop);
prototype.set_method("toString", to_string);
let prototype = to_value(Object::default());
object.set_field_slice(PROTOTYPE, prototype.clone());
make_builtin_fn!(has_own_prop, named "hasOwnProperty", of prototype);
make_builtin_fn!(to_string, named "toString", of prototype);
object.set_field_slice("length", to_value(1_i32));
object.set_field_slice(PROTOTYPE, to_value(prototype));
make_builtin_fn!(set_proto_of, named "setPrototypeOf", with length 2, of object);
make_builtin_fn!(get_proto_of, named "getPrototypeOf", with length 1, of object);
make_builtin_fn!(define_prop, named "defineProperty", with length 3, of object);

69
boa/src/builtins/regexp/mod.rs

@ -16,8 +16,7 @@ use regex::Regex;
use crate::{
builtins::{
function::NativeFunctionData,
object::{InternalState, Object, ObjectKind, PROTOTYPE},
object::{InternalState, Object, ObjectInternalMethods, ObjectKind, PROTOTYPE},
property::Property,
value::{from_value, to_value, FromValue, ResultValue, Value, ValueData},
},
@ -66,7 +65,7 @@ fn get_argument<T: FromValue>(args: &[Value], idx: usize) -> Result<T, Value> {
}
/// Create a new `RegExp`
pub fn make_regexp(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn make_regexp(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
if args.is_empty() {
return Err(Gc::new(ValueData::Undefined));
}
@ -181,7 +180,7 @@ pub fn make_regexp(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultV
///
/// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.dotAll
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/dotAll
fn get_dot_all(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
fn get_dot_all(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.dot_all)))
}
@ -196,7 +195,7 @@ fn get_dot_all(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.flags
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/flags
/// [flags]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Advanced_searching_with_flags_2
fn get_flags(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
fn get_flags(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.flags.clone())))
}
@ -210,7 +209,7 @@ fn get_flags(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.global
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/global
fn get_global(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
fn get_global(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.global)))
}
@ -224,7 +223,7 @@ fn get_global(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.ignorecase
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/ignoreCase
fn get_ignore_case(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
fn get_ignore_case(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.ignore_case)))
}
@ -238,7 +237,7 @@ fn get_ignore_case(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValu
///
/// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.multiline
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/multiline
fn get_multiline(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
fn get_multiline(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.multiline)))
}
@ -253,7 +252,7 @@ fn get_multiline(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue
///
/// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.source
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/source
fn get_source(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
fn get_source(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(this.get_internal_slot("OriginalSource"))
}
@ -267,7 +266,7 @@ fn get_source(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.sticky
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/sticky
fn get_sticky(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
fn get_sticky(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.sticky)))
}
@ -282,15 +281,10 @@ fn get_sticky(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.unicode
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/unicode
fn get_unicode(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
fn get_unicode(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.unicode)))
}
/// Helper function.
fn _make_prop(getter: NativeFunctionData) -> Property {
Property::default().get(to_value(getter))
}
/// `RegExp.prototype.test( string )`
///
/// The `test()` method executes a search for a match between a regular expression and a specified string.
@ -303,7 +297,7 @@ fn _make_prop(getter: NativeFunctionData) -> Property {
///
/// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype.test
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test
pub fn test(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn test(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let arg_str = get_argument::<String>(args, 0)?;
let mut last_index =
from_value::<usize>(this.get_field_slice("lastIndex")).map_err(to_value)?;
@ -337,7 +331,7 @@ pub fn test(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype.exec
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec
pub fn exec(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn exec(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let arg_str = get_argument::<String>(args, 0)?;
let mut last_index =
from_value::<usize>(this.get_field_slice("lastIndex")).map_err(to_value)?;
@ -387,7 +381,7 @@ pub fn exec(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype-@@match
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/@@match
pub fn r#match(this: &Value, arg: String, ctx: &mut Interpreter) -> ResultValue {
pub fn r#match(this: &mut Value, arg: String, ctx: &mut Interpreter) -> ResultValue {
let (matcher, flags) =
this.with_internal_state_ref(|regex: &RegExp| (regex.matcher.clone(), regex.flags.clone()));
if flags.contains('g') {
@ -414,7 +408,7 @@ pub fn r#match(this: &Value, arg: String, ctx: &mut Interpreter) -> ResultValue
///
/// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/toString
pub fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let body = from_value::<String>(this.get_internal_slot("OriginalSource")).map_err(to_value)?;
let flags = this.with_internal_state_ref(|regex: &RegExp| regex.flags.clone());
Ok(to_value(format!("/{}/{}", body, flags)))
@ -431,7 +425,7 @@ pub fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue
/// [spec]: https://tc39.es/ecma262/#sec-regexp-prototype-matchall
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/@@matchAll
// TODO: it's returning an array, it should return an iterator
pub fn match_all(this: &Value, arg_str: String) -> ResultValue {
pub fn match_all(this: &mut Value, arg_str: String) -> ResultValue {
let matches: Vec<Value> = this.with_internal_state_ref(|regex: &RegExp| {
let mut matches = Vec::new();
@ -473,32 +467,23 @@ pub fn match_all(this: &Value, arg_str: String) -> ResultValue {
/// Create a new `RegExp` object.
pub fn create_constructor(global: &Value) -> Value {
// Create constructor function
let mut regexp_constructor = Object::default();
regexp_constructor.kind = ObjectKind::Function;
regexp_constructor.set_internal_method("construct", make_regexp);
// Todo: add call function, currently call points to contructor, this is wrong
regexp_constructor.set_internal_method("call", make_regexp);
// Create prototype
let proto = ValueData::new_obj(Some(global));
proto.set_field_slice("lastIndex", to_value(0));
make_builtin_fn!(test, named "test", with length 1, of proto);
make_builtin_fn!(exec, named "exec", with length 1, of proto);
make_builtin_fn!(to_string, named "toString", of proto);
proto.set_field_slice("lastIndex", to_value(0));
proto.set_prop_slice("dotAll", _make_prop(get_dot_all));
proto.set_prop_slice("flags", _make_prop(get_flags));
proto.set_prop_slice("global", _make_prop(get_global));
proto.set_prop_slice("ignoreCase", _make_prop(get_ignore_case));
proto.set_prop_slice("multiline", _make_prop(get_multiline));
proto.set_prop_slice("source", _make_prop(get_source));
proto.set_prop_slice("sticky", _make_prop(get_sticky));
proto.set_prop_slice("unicode", _make_prop(get_unicode));
let regexp = to_value(regexp_constructor);
regexp.set_field_slice(PROTOTYPE, proto.clone());
proto.set_field_slice("constructor", regexp.clone());
regexp
make_builtin_fn!(get_dot_all, named "dotAll", of proto);
make_builtin_fn!(get_flags, named "flags", of proto);
make_builtin_fn!(get_global, named "global", of proto);
make_builtin_fn!(get_ignore_case, named "ignoreCase", of proto);
make_builtin_fn!(get_multiline, named "multiline", of proto);
make_builtin_fn!(get_source, named "source", of proto);
make_builtin_fn!(get_sticky, named "sticky", of proto);
make_builtin_fn!(get_unicode, named "unicode", of proto);
make_constructor_fn!(make_regexp, make_regexp, global, proto)
}
#[cfg(test)]

87
boa/src/builtins/string/mod.rs

@ -14,8 +14,7 @@ mod tests;
use crate::{
builtins::{
function::NativeFunctionData,
object::{Object, ObjectKind, PROTOTYPE},
object::{internal_methods_trait::ObjectInternalMethods, Object, ObjectKind, PROTOTYPE},
property::Property,
regexp::{make_regexp, match_all as regexp_match_all, r#match as regexp_match},
value::{from_value, to_value, ResultValue, Value, ValueData},
@ -32,7 +31,7 @@ use std::{
/// Create new string [[Construct]]
// This gets called when a new String() is created, it's called by exec:346
pub fn make_string(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn make_string(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
// If we're constructing a string, we should set the initial length
// To do this we need to convert the string back to a Rust String, then get the .len()
// let a: String = from_value(args.get(0).expect("failed to get argument for String method").clone()).unwrap();
@ -53,7 +52,7 @@ pub fn make_string(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultV
/// Call new string [[Call]]
///
/// More information: [ECMAScript reference](https://tc39.es/ecma262/#sec-string-constructor-string-value)
pub fn call_string(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn call_string(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let arg = match args.get(0) {
Some(v) => v.clone(),
None => Gc::new(ValueData::Undefined),
@ -67,7 +66,7 @@ pub fn call_string(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValu
}
/// Get the string value to a primitive string
pub fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
// Get String from String Object and send it back as a new value
let primitive_val = this.get_internal_slot("StringData");
Ok(to_value(format!("{}", primitive_val)))
@ -89,7 +88,7 @@ pub fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.charat
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charAt
pub fn char_at(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
pub fn char_at(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
// First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = ctx.value_to_rust_string(this);
@ -133,7 +132,7 @@ pub fn char_at(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultVal
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.charcodeat
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charCodeAt
pub fn char_code_at(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
pub fn char_code_at(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
// First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val: String = ctx.value_to_rust_string(this);
@ -175,7 +174,7 @@ pub fn char_code_at(this: &Value, args: &[Value], ctx: &mut Interpreter) -> Resu
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.concat
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/concat
pub fn concat(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
pub fn concat(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
// First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value
let mut new_str = ctx.value_to_rust_string(this);
@ -199,7 +198,7 @@ pub fn concat(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValu
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.repeat
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat
pub fn repeat(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
pub fn repeat(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
// First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val: String = ctx.value_to_rust_string(this);
@ -223,7 +222,7 @@ pub fn repeat(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValu
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.slice
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/slice
pub fn slice(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
pub fn slice(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
// First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val: String = ctx.value_to_rust_string(this);
@ -280,7 +279,7 @@ pub fn slice(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.startswith
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith
pub fn starts_with(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
pub fn starts_with(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
// First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val: String = ctx.value_to_rust_string(this);
@ -325,7 +324,7 @@ pub fn starts_with(this: &Value, args: &[Value], ctx: &mut Interpreter) -> Resul
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.endswith
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith
pub fn ends_with(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
pub fn ends_with(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
// First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val: String = ctx.value_to_rust_string(this);
@ -372,7 +371,7 @@ pub fn ends_with(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultV
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.includes
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes
pub fn includes(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
pub fn includes(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
// First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val: String = ctx.value_to_rust_string(this);
@ -437,7 +436,7 @@ fn get_regex_string(value: &Value) -> String {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.replace
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace
pub fn replace(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
pub fn replace(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
// TODO: Support Symbol replacer
let primitive_val: String = ctx.value_to_rust_string(this);
if args.is_empty() {
@ -494,7 +493,7 @@ pub fn replace(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultVal
result
}
ValueData::Function(_) => {
ValueData::Object(_) => {
// This will return the matched substring first, then captured parenthesized groups later
let mut results: Vec<Value> = caps
.iter()
@ -510,7 +509,7 @@ pub fn replace(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultVal
// Push the whole string being examined
results.push(to_value(primitive_val.to_string()));
let result = ctx.call(&replace_object, &this, results).unwrap();
let result = ctx.call(&replace_object, this, &results).unwrap();
ctx.value_to_rust_string(&result)
}
@ -539,7 +538,7 @@ pub fn replace(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultVal
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.indexof
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf
pub fn index_of(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
pub fn index_of(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
// First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val: String = ctx.value_to_rust_string(this);
@ -591,7 +590,7 @@ pub fn index_of(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultVa
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.lastindexof
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/lastIndexOf
pub fn last_index_of(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
pub fn last_index_of(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
// First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val: String = ctx.value_to_rust_string(this);
@ -643,9 +642,9 @@ pub fn last_index_of(this: &Value, args: &[Value], ctx: &mut Interpreter) -> Res
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.match
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match
/// [regex]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
pub fn r#match(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let re = make_regexp(&to_value(Object::default()), &[args[0].clone()], ctx)?;
regexp_match(&re, ctx.value_to_rust_string(this), ctx)
pub fn r#match(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let mut re = make_regexp(&mut to_value(Object::default()), &[args[0].clone()], ctx)?;
regexp_match(&mut re, ctx.value_to_rust_string(this), ctx)
}
/// Abstract method `StringPad`.
@ -701,7 +700,7 @@ fn string_pad(
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.padend
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd
pub fn pad_end(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
pub fn pad_end(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let primitive_val: String = ctx.value_to_rust_string(this);
if args.is_empty() {
return Err(to_value("padEnd requires maxLength argument"));
@ -735,7 +734,7 @@ pub fn pad_end(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultVal
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.padstart
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart
pub fn pad_start(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
pub fn pad_start(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let primitive_val: String = ctx.value_to_rust_string(this);
if args.is_empty() {
return Err(to_value("padStart requires maxLength argument"));
@ -788,7 +787,7 @@ fn is_trimmable_whitespace(c: char) -> bool {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.trim
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trim
pub fn trim(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
pub fn trim(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
let this_str: String = ctx.value_to_rust_string(this);
Ok(to_value(this_str.trim_matches(is_trimmable_whitespace)))
}
@ -805,7 +804,7 @@ pub fn trim(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.trimstart
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimStart
pub fn trim_start(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
pub fn trim_start(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
let this_str: String = ctx.value_to_rust_string(this);
Ok(to_value(
this_str.trim_start_matches(is_trimmable_whitespace),
@ -824,7 +823,7 @@ pub fn trim_start(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultVal
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.trimend
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimEnd
pub fn trim_end(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
pub fn trim_end(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
let this_str: String = ctx.value_to_rust_string(this);
Ok(to_value(this_str.trim_end_matches(is_trimmable_whitespace)))
}
@ -839,7 +838,7 @@ pub fn trim_end(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.tolowercase
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toLowerCase
pub fn to_lowercase(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
pub fn to_lowercase(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
// First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value
let this_str: String = ctx.value_to_rust_string(this);
@ -860,7 +859,7 @@ pub fn to_lowercase(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultV
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.toUppercase
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase
pub fn to_uppercase(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
pub fn to_uppercase(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
// First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value
let this_str: String = ctx.value_to_rust_string(this);
@ -879,7 +878,7 @@ pub fn to_uppercase(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultV
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.substring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substring
pub fn substring(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
pub fn substring(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
// First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val: String = ctx.value_to_rust_string(this);
@ -930,7 +929,7 @@ pub fn substring(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultV
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.substr
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substr
/// <https://tc39.es/ecma262/#sec-string.prototype.substr>
pub fn substr(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
pub fn substr(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
// First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val: String = ctx.value_to_rust_string(this);
@ -987,7 +986,7 @@ pub fn substr(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValu
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.value_of
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/valueOf
pub fn value_of(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
pub fn value_of(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
// Use the to_string method because it is specified to do the same thing in this case
to_string(this, args, ctx)
}
@ -1005,12 +1004,12 @@ pub fn value_of(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultVa
/// [regex]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
/// [cg]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Groups_and_Ranges
// TODO: update this method to return iterator
pub fn match_all(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let re: Value = match args.get(0) {
pub fn match_all(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let mut re: Value = match args.get(0) {
Some(arg) => {
if arg == &Gc::new(ValueData::Null) {
make_regexp(
&to_value(Object::default()),
&mut to_value(Object::default()),
&[
to_value(ctx.value_to_rust_string(arg)),
to_value(String::from("g")),
@ -1019,7 +1018,7 @@ pub fn match_all(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultV
)
} else if arg == &Gc::new(ValueData::Undefined) {
make_regexp(
&to_value(Object::default()),
&mut to_value(Object::default()),
&[Gc::new(ValueData::Undefined), to_value(String::from("g"))],
ctx,
)
@ -1028,26 +1027,17 @@ pub fn match_all(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultV
}
}
None => make_regexp(
&to_value(Object::default()),
&mut to_value(Object::default()),
&[to_value(String::new()), to_value(String::from("g"))],
ctx,
),
}?;
regexp_match_all(&re, ctx.value_to_rust_string(this))
regexp_match_all(&mut re, ctx.value_to_rust_string(this))
}
/// Create a new `String` object
pub fn create_constructor(global: &Value) -> Value {
// Create constructor function object
let mut string_constructor = Object::default();
string_constructor.kind = ObjectKind::Function;
string_constructor.set_internal_method("construct", make_string);
// Todo: add call internal method (should be easy)
// Currently call points to the constructor function, this is wrong
string_constructor.set_internal_method("call", call_string);
// Create prototype
let proto = ValueData::new_obj(Some(global));
let prop = Property::default().value(to_value(0_i32));
@ -1077,10 +1067,7 @@ pub fn create_constructor(global: &Value) -> Value {
make_builtin_fn!(match_all, named "matchAll", with length 1, of proto);
make_builtin_fn!(replace, named "replace", with length 2, of proto);
let string = to_value(string_constructor);
proto.set_field_slice("constructor", string.clone());
string.set_field_slice(PROTOTYPE, proto);
string
make_constructor_fn!(make_string, call_string, global, proto)
}
/// Initialise the `String` object on the global object

3
boa/src/builtins/string/tests.rs

@ -10,7 +10,7 @@ fn check_string_constructor_is_function() {
assert_eq!(string_constructor.is_function(), true);
}
#[test]
// #[test]
// TODO: re-enable when getProperty() is finished;
// fn length() {
// //TEST262: https://github.com/tc39/test262/blob/master/test/built-ins/String/length.js
@ -35,6 +35,7 @@ fn check_string_constructor_is_function() {
// let d = forward(&mut engine, "d.length");
// assert_eq!(d, String::from("4"));
// }
#[test]
fn concat() {
let realm = Realm::create();

32
boa/src/builtins/symbol/mod.rs

@ -41,7 +41,7 @@ use rand::random;
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-symbol-description
pub fn call_symbol(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
pub fn call_symbol(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
// From an implementation and specificaition perspective Symbols are similar to Objects.
// They have internal slots to hold the SymbolData and Description, they also have methods and a prototype.
// So we start by creating an Object
@ -66,7 +66,9 @@ pub fn call_symbol(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultVa
.get_field_slice(PROTOTYPE);
sym_instance.set_internal_slot(INSTANCE_PROTOTYPE, proto);
Ok(Gc::new(ValueData::Symbol(GcCell::new(sym_instance))))
Ok(Gc::new(ValueData::Symbol(Box::new(GcCell::new(
sym_instance,
)))))
}
/// `Symbol.prototype.toString()`
@ -79,7 +81,7 @@ pub fn call_symbol(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultVa
///
/// [spec]: https://tc39.es/ecma262/#sec-symbol.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toString
pub fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let s: Value = this.get_internal_slot("Description");
let full_string = format!(r#"Symbol({})"#, s.to_string());
Ok(to_value(full_string))
@ -96,24 +98,8 @@ pub fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue
/// [spec]: https://tc39.es/ecma262/#sec-symbol-constructor
/// [mdn]:
pub fn create_constructor(global: &Value) -> Value {
// Create Symbol constructor (or function in Symbol's case)
let mut symbol_constructor = Object::default();
symbol_constructor.set_internal_method("call", call_symbol);
// Create prototype
let mut symbol_prototype = Object::default();
// Symbol.prototype[[Prototype]] points to Object.prototype
// Symbol Constructor -> Symbol Prototype -> Object Prototype
let object_prototype = global.get_field_slice("Object").get_field_slice(PROTOTYPE);
symbol_prototype.set_internal_slot(INSTANCE_PROTOTYPE, object_prototype);
symbol_prototype.set_method("toString", to_string);
let symbol_prototype_val = to_value(symbol_prototype);
let symbol_constructor_value = to_value(symbol_constructor);
symbol_prototype_val.set_field_slice("construcotor", symbol_constructor_value.clone());
symbol_constructor_value.set_field_slice(PROTOTYPE, symbol_prototype_val);
symbol_constructor_value
// Create prototype object
let proto = ValueData::new_obj(Some(global));
make_builtin_fn!(to_string, named "toString", of proto);
make_constructor_fn!(call_symbol, call_symbol, global, proto)
}

191
boa/src/builtins/value/mod.rs

@ -6,7 +6,7 @@
mod tests;
use crate::builtins::{
function::{Function, NativeFunction, NativeFunctionData},
function::Function,
object::{
internal_methods_trait::ObjectInternalMethods, InternalState, InternalStateCell, Object,
ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE,
@ -52,11 +52,9 @@ pub enum ValueData {
/// `Number` - A 32-bit integer, such as `42`
Integer(i32),
/// `Object` - An object, such as `Math`, represented by a binary tree of string keys to Javascript values
Object(GcCell<Object>),
/// `Function` - A runnable block of code, such as `Math.sqrt`, which can take some variables and return a useful value or act upon an object
Function(Box<GcCell<Function>>),
Object(Box<GcCell<Object>>),
/// `Symbol` - A Symbol Type - Internally Symbols are similar to objects, except there are no properties, only internal slots
Symbol(GcCell<Object>),
Symbol(Box<GcCell<Object>>),
}
impl ValueData {
@ -66,10 +64,10 @@ impl ValueData {
let obj_proto = glob.get_field_slice("Object").get_field_slice(PROTOTYPE);
let obj = Object::create(obj_proto);
Gc::new(Self::Object(GcCell::new(obj)))
Gc::new(Self::Object(Box::new(GcCell::new(obj))))
} else {
let obj = Object::default();
Gc::new(Self::Object(GcCell::new(obj)))
Gc::new(Self::Object(Box::new(GcCell::new(obj))))
}
}
@ -82,7 +80,7 @@ impl ValueData {
obj.internal_slots
.insert(INSTANCE_PROTOTYPE.to_string(), proto);
Gc::new(Self::Object(GcCell::new(obj)))
Gc::new(Self::Object(Box::new(GcCell::new(obj))))
}
/// This will tell us if we can exten an object or not, not properly implemented yet
@ -117,8 +115,10 @@ impl ValueData {
/// Returns true if the value is a function
pub fn is_function(&self) -> bool {
match *self {
Self::Function(_) => true,
Self::Object(ref o) => o.deref().borrow().get_internal_slot("call").is_function(),
Self::Object(ref o) => {
let borrowed_obj = o.borrow();
borrowed_obj.is_callable() || borrowed_obj.is_constructor()
}
_ => false,
}
}
@ -207,14 +207,14 @@ impl ValueData {
/// Converts the value into a 64-bit floating point number
pub fn to_num(&self) -> f64 {
match *self {
Self::Object(_) | Self::Symbol(_) | Self::Undefined | Self::Function(_) => NAN,
Self::Object(_) | Self::Symbol(_) | Self::Undefined => NAN,
Self::String(ref str) => match FromStr::from_str(str) {
Ok(num) => num,
Err(_) => NAN,
},
Self::Rational(num) => num,
Self::Boolean(true) => 1.0,
Self::Boolean(false) | ValueData::Null => 0.0,
Self::Boolean(false) | Self::Null => 0.0,
Self::Integer(num) => f64::from(num),
}
}
@ -226,8 +226,7 @@ impl ValueData {
| Self::Undefined
| Self::Symbol(_)
| Self::Null
| Self::Boolean(false)
| Self::Function(_) => 0,
| Self::Boolean(false) => 0,
Self::String(ref str) => match FromStr::from_str(str) {
Ok(num) => num,
Err(_) => 0,
@ -251,11 +250,6 @@ impl ValueData {
pub fn remove_prop(&self, field: &str) {
match *self {
Self::Object(ref obj) => obj.borrow_mut().deref_mut().properties.remove(field),
// Accesing .object on borrow() seems to automatically dereference it, so we don't need the *
Self::Function(ref func) => match func.borrow_mut().deref_mut() {
Function::NativeFunc(ref mut func) => func.object.properties.remove(field),
Function::RegularFunc(ref mut func) => func.object.properties.remove(field),
},
_ => None,
};
}
@ -272,6 +266,10 @@ impl ValueData {
}
}
if self.is_undefined() {
return None;
}
let obj: Object = match *self {
Self::Object(ref obj) => {
let hash = obj.clone();
@ -279,11 +277,6 @@ impl ValueData {
// into_inner will consume the wrapped value and remove it from the hashmap
hash.into_inner()
}
// Accesing .object on borrow() seems to automatically dereference it, so we don't need the *
Self::Function(ref func) => match func.clone().into_inner() {
Function::NativeFunc(ref func) => func.object.clone(),
Function::RegularFunc(ref func) => func.object.clone(),
},
Self::Symbol(ref obj) => {
let hash = obj.clone();
hash.into_inner()
@ -313,11 +306,6 @@ impl ValueData {
) {
let obj: Option<Object> = match self {
Self::Object(ref obj) => Some(obj.borrow_mut().deref_mut().clone()),
// Accesing .object on borrow() seems to automatically dereference it, so we don't need the *
Self::Function(ref func) => match func.borrow_mut().deref_mut() {
Function::NativeFunc(ref mut func) => Some(func.object.clone()),
Function::RegularFunc(ref mut func) => Some(func.object.clone()),
},
_ => None,
};
@ -473,47 +461,33 @@ impl ValueData {
/// Set the field in the value
/// Field could be a Symbol, so we need to accept a Value (not a string)
pub fn set_field(&self, field: Value, val: Value) -> Value {
match *self {
Self::Object(ref obj) => {
if obj.borrow().kind == ObjectKind::Array {
if let Ok(num) = field.to_string().parse::<usize>() {
if num > 0 {
let len: i32 = from_value(self.get_field_slice("length"))
.expect("Could not convert argument to i32");
if len < (num + 1) as i32 {
self.set_field_slice("length", to_value(num + 1));
}
if let Self::Object(ref obj) = *self {
if obj.borrow().kind == ObjectKind::Array {
if let Ok(num) = field.to_string().parse::<usize>() {
if num > 0 {
let len: i32 = from_value(self.get_field_slice("length"))
.expect("Could not convert argument to i32");
if len < (num + 1) as i32 {
self.set_field_slice("length", to_value(num + 1));
}
}
}
// Symbols get saved into a different bucket to general properties
if field.is_symbol() {
obj.borrow_mut().set(field, val.clone());
} else {
obj.borrow_mut()
.set(to_value(field.to_string()), val.clone());
}
}
Self::Function(ref func) => {
match *func.borrow_mut().deref_mut() {
Function::NativeFunc(ref mut f) => f
.object
.properties
.insert(field.to_string(), Property::default().value(val.clone())),
Function::RegularFunc(ref mut f) => f
.object
.properties
.insert(field.to_string(), Property::default().value(val.clone())),
};
// Symbols get saved into a different bucket to general properties
if field.is_symbol() {
obj.borrow_mut().set(field, val.clone());
} else {
obj.borrow_mut()
.set(to_value(field.to_string()), val.clone());
}
_ => (),
}
val
}
/// Set the field in the value
pub fn set_field_slice<'a>(&self, field: &'a str, val: Value) -> Value {
pub fn set_field_slice(&self, field: &str, val: Value) -> Value {
// set_field used to accept strings, but now Symbols accept it needs to accept a value
// So this function will now need to Box strings back into values (at least for now)
let f = Gc::new(Self::String(field.to_string()));
@ -531,36 +505,22 @@ impl ValueData {
}
/// Set the kind of an object
pub fn set_kind(&self, kind: ObjectKind) -> ObjectKind {
pub fn set_kind(&self, kind: ObjectKind) {
if let Self::Object(ref obj) = *self {
obj.borrow_mut().kind = kind.clone();
(*obj.deref().borrow_mut()).kind = kind;
}
kind
}
/// Set the property in the value
pub fn set_prop(&self, field: String, prop: Property) -> Property {
match *self {
Self::Object(ref obj) => {
obj.borrow_mut().properties.insert(field, prop.clone());
}
Self::Function(ref func) => {
match *func.borrow_mut().deref_mut() {
Function::NativeFunc(ref mut f) => {
f.object.properties.insert(field, prop.clone())
}
Function::RegularFunc(ref mut f) => {
f.object.properties.insert(field, prop.clone())
}
};
}
_ => (),
if let Self::Object(ref obj) = *self {
obj.borrow_mut().properties.insert(field, prop.clone());
}
prop
}
/// Set the property in the value
pub fn set_prop_slice<'t>(&self, field: &'t str, prop: Property) -> Property {
pub fn set_prop_slice(&self, field: &str, prop: Property) -> Property {
self.set_prop(field.to_string(), prop)
}
@ -573,6 +533,21 @@ impl ValueData {
}
}
/// Consume the function and return a Value
pub fn from_func(native_func: Function) -> Value {
// Object with Kind set to function
let mut new_func = crate::builtins::object::Object::function();
// Get Length
let length = native_func.params.len();
// Set [[Call]] internal slot
new_func.set_call(native_func);
// Wrap Object in GC'd Value
let new_func_val = to_value(new_func);
// Set length to parameters
new_func_val.set_field_slice("length", to_value(length));
new_func_val
}
/// Convert from a JSON value to a JS value
pub fn from_json(json: JSONValue) -> Self {
match json {
@ -593,7 +568,7 @@ impl ValueData {
"length".to_string(),
Property::default().value(to_value(vs.len() as i32)),
);
Self::Object(GcCell::new(new_obj))
Self::Object(Box::new(GcCell::new(new_obj)))
}
JSONValue::Object(obj) => {
let mut new_obj = Object::default();
@ -604,7 +579,7 @@ impl ValueData {
);
}
Self::Object(GcCell::new(new_obj))
Self::Object(Box::new(GcCell::new(new_obj)))
}
JSONValue::Null => Self::Null,
}
@ -613,12 +588,9 @@ impl ValueData {
/// Conversts the `Value` to `JSON`.
pub fn to_json(&self) -> JSONValue {
match *self {
ValueData::Null
| ValueData::Symbol(_)
| ValueData::Undefined
| ValueData::Function(_) => JSONValue::Null,
ValueData::Boolean(b) => JSONValue::Bool(b),
ValueData::Object(ref obj) => {
Self::Null | Self::Symbol(_) | Self::Undefined => JSONValue::Null,
Self::Boolean(b) => JSONValue::Bool(b),
Self::Object(ref obj) => {
let new_obj = obj
.borrow()
.properties
@ -646,12 +618,11 @@ impl ValueData {
Self::Symbol(_) => "symbol",
Self::Null => "null",
Self::Undefined => "undefined",
Self::Function(_) => "function",
Self::Object(ref o) => {
if o.deref().borrow().get_internal_slot("call").is_null() {
"object"
} else {
if o.deref().borrow().is_callable() {
"function"
} else {
"object"
}
}
}
@ -880,19 +851,6 @@ impl Display for ValueData {
),
Self::Object(_) => write!(f, "{}", log_string_from(self, true)),
Self::Integer(v) => write!(f, "{}", v),
Self::Function(ref v) => match *v.borrow() {
Function::NativeFunc(_) => write!(f, "function() {{ [native code] }}"),
Function::RegularFunc(ref rf) => {
write!(f, "function{}(", if rf.args.is_empty() { "" } else { " " })?;
for (index, arg) in rf.args.iter().enumerate() {
write!(f, "{}", arg)?;
if index + 1 != rf.args.len() {
write!(f, ", ")?;
}
}
write!(f, ") {}", rf.node)
}
},
}
}
}
@ -1124,7 +1082,7 @@ impl<T: FromValue> FromValue for Vec<T> {
impl ToValue for Object {
fn to_value(&self) -> Value {
Gc::new(ValueData::Object(GcCell::new(self.clone())))
Gc::new(ValueData::Object(Box::new(GcCell::new(self.clone()))))
}
}
@ -1132,10 +1090,6 @@ impl FromValue for Object {
fn from_value(v: Value) -> Result<Self, &'static str> {
match *v {
ValueData::Object(ref obj) => Ok(obj.clone().into_inner()),
ValueData::Function(ref func) => Ok(match *func.borrow().deref() {
Function::NativeFunc(ref data) => data.object.clone(),
Function::RegularFunc(ref data) => data.object.clone(),
}),
_ => Err("Value is not a valid object"),
}
}
@ -1182,25 +1136,6 @@ impl<T: FromValue> FromValue for Option<T> {
}
}
impl ToValue for NativeFunctionData {
fn to_value(&self) -> Value {
Gc::new(ValueData::Function(Box::new(GcCell::new(
Function::NativeFunc(NativeFunction::new(*self)),
))))
}
}
impl FromValue for NativeFunctionData {
fn from_value(v: Value) -> Result<Self, &'static str> {
match *v {
ValueData::Function(ref func) => match *func.borrow() {
Function::NativeFunc(ref data) => Ok(data.data),
_ => Err("Value is not a native function"),
},
_ => Err("Value is not a function"),
}
}
}
/// A utility function that just calls `FromValue::from_value`
pub fn from_value<A: FromValue>(v: Value) -> Result<A, &'static str> {
FromValue::from_value(v)

2
boa/src/environment/function_environment_record.rs

@ -41,7 +41,7 @@ pub struct FunctionEnvironmentRecord {
/// If the value is "lexical", this is an ArrowFunction and does not have a local this value.
pub this_binding_status: BindingStatus,
/// The function object whose invocation caused this Environment Record to be created.
pub function_object: Value,
pub function: Value,
/// If the associated function has super property accesses and is not an ArrowFunction,
/// [[HomeObject]] is the object that the function is bound to as a method.
/// The default value for [[HomeObject]] is undefined.

3
boa/src/environment/lexical_environment.rs

@ -229,11 +229,10 @@ pub fn new_function_environment(
new_target: Value,
outer: Option<Environment>,
) -> Environment {
debug_assert!(f.is_function());
debug_assert!(new_target.is_object() || new_target.is_undefined());
Gc::new(GcCell::new(Box::new(FunctionEnvironmentRecord {
env_rec: HashMap::new(),
function_object: f,
function: f,
this_binding_status: BindingStatus::Uninitialized, // hardcoding to unitialized for now until short functions are properly supported
home_object: Gc::new(ValueData::Undefined),
new_target,

250
boa/src/exec/mod.rs

@ -6,17 +6,15 @@ mod tests;
use crate::{
builtins::{
array,
function::{create_unmapped_arguments_object, Function, RegularFunction},
function::{Function as FunctionObject, FunctionBody, ThisMode},
object::{
internal_methods_trait::ObjectInternalMethods, ObjectKind, INSTANCE_PROTOTYPE,
internal_methods_trait::ObjectInternalMethods, Object, ObjectKind, INSTANCE_PROTOTYPE,
PROTOTYPE,
},
property::Property,
value::{from_value, to_value, ResultValue, Value, ValueData},
},
environment::lexical_environment::{
new_declarative_environment, new_function_environment, VariableScope,
},
environment::lexical_environment::{new_declarative_environment, VariableScope},
realm::Realm,
syntax::ast::{
constant::Const,
@ -24,10 +22,10 @@ use crate::{
op::{AssignOp, BinOp, BitOp, CompOp, LogOp, NumOp, UnaryOp},
},
};
use gc::{Gc, GcCell};
use gc::Gc;
use std::{
borrow::Borrow,
ops::{Deref, DerefMut},
borrow::{Borrow, BorrowMut},
ops::Deref,
};
/// An execution engine
@ -124,7 +122,7 @@ impl Executor for Interpreter {
.get_field_slice(&val_field.borrow().to_string()))
}
Node::Call(ref callee, ref args) => {
let (this, func) = match callee.deref() {
let (mut this, func) = match callee.deref() {
Node::GetConstField(ref obj, ref field) => {
let mut obj = self.run(obj)?;
if obj.get_type() != "object" || obj.get_type() != "symbol" {
@ -154,7 +152,7 @@ impl Executor for Interpreter {
}
// execute the function call itself
let fnct_result = self.call(&func, &this, v_args);
let fnct_result = self.call(&func, &mut this, &v_args);
// unset the early return flag
self.is_return = false;
@ -258,29 +256,68 @@ impl Executor for Interpreter {
array::add_to_array_object(&array, &elements)?;
Ok(array)
}
// <https://tc39.es/ecma262/#sec-createdynamicfunction>
Node::FunctionDecl(ref name, ref args, ref expr) => {
let function =
Function::RegularFunc(RegularFunction::new(*expr.clone(), args.to_vec()));
let val = Gc::new(ValueData::Function(Box::new(GcCell::new(function))));
// Todo: Function.prototype doesn't exist yet, so the prototype right now is the Object.prototype
// let proto = &self
// .realm
// .environment
// .get_global_object()
// .expect("Could not get the global object")
// .get_field_slice("Object")
// .get_field_slice("Prototype");
let func = FunctionObject::create_ordinary(
args.clone(), // TODO: args shouldn't need to be a reference it should be passed by value
self.realm.environment.get_current_environment().clone(),
FunctionBody::Ordinary(*expr.clone()),
ThisMode::NonLexical,
);
let mut new_func = Object::function();
new_func.set_call(func);
let val = to_value(new_func);
val.set_field_slice("length", to_value(args.len()));
// Set the name and assign it in the current environment
if name.is_some() {
self.realm.environment.create_mutable_binding(
name.clone().expect("No name was supplied"),
false,
VariableScope::Function,
);
self.realm.environment.initialize_binding(
name.as_ref().expect("Could not get name as reference"),
val.clone(),
)
}
Ok(val)
}
Node::ArrowFunctionDecl(ref args, ref expr) => {
let function =
Function::RegularFunc(RegularFunction::new(*expr.clone(), args.to_vec()));
Ok(Gc::new(ValueData::Function(Box::new(GcCell::new(
function,
)))))
// Todo: Function.prototype doesn't exist yet, so the prototype right now is the Object.prototype
// let proto = &self
// .realm
// .environment
// .get_global_object()
// .expect("Could not get the global object")
// .get_field_slice("Object")
// .get_field_slice("Prototype");
let func = FunctionObject::create_ordinary(
args.clone(), // TODO: args shouldn't need to be a reference it should be passed by value
self.realm.environment.get_current_environment().clone(),
FunctionBody::Ordinary(*expr.clone()),
ThisMode::Lexical,
);
let mut new_func = Object::function();
new_func.set_call(func);
let val = to_value(new_func);
val.set_field_slice("length", to_value(args.len()));
Ok(val)
}
Node::BinOp(BinOp::Num(ref op), ref a, ref b) => {
let v_r_a = self.run(a)?;
@ -331,10 +368,10 @@ impl Executor for Interpreter {
}))
}
Node::BinOp(BinOp::Comp(ref op), ref a, ref b) => {
let v_r_a = self.run(a)?;
let v_r_b = self.run(b)?;
let v_a = v_r_a.borrow();
let v_b = v_r_b.borrow();
let mut v_r_a = self.run(a)?;
let mut v_r_b = self.run(b)?;
let mut v_a = v_r_a.borrow_mut();
let mut v_b = v_r_b.borrow_mut();
Ok(to_value(match *op {
CompOp::Equal if v_a.is_object() => v_r_a == v_r_b,
CompOp::Equal => v_a == v_b,
@ -352,8 +389,8 @@ impl Executor for Interpreter {
if !v_b.is_object() {
panic!("TypeError: {} is not an Object.", v_b);
}
let key = self.to_property_key(v_a);
self.has_property(v_b, &key)
let key = self.to_property_key(&mut v_a);
self.has_property(&mut v_b, &key)
}
}))
}
@ -399,54 +436,19 @@ impl Executor for Interpreter {
for arg in args.iter() {
v_args.push(self.run(arg)?);
}
let this = ValueData::new_obj(None);
let mut this = ValueData::new_obj(None);
// Create a blank object, then set its __proto__ property to the [Constructor].prototype
this.borrow().set_internal_slot(
INSTANCE_PROTOTYPE,
func_object.borrow().get_field_slice(PROTOTYPE),
);
let construct = func_object.get_internal_slot("construct");
match *construct {
ValueData::Function(ref inner_func) => match inner_func.clone().into_inner() {
Function::NativeFunc(ref ntv) => {
let func = ntv.data;
match func(&this, &v_args, self) {
Ok(_) => Ok(this),
Err(ref v) => Err(v.clone()),
}
}
Function::RegularFunc(ref data) => {
// Create new scope
let env = &mut self.realm.environment;
env.push(new_function_environment(
construct.clone(),
this,
Some(env.get_current_environment_ref().clone()),
));
for i in 0..data.args.len() {
let arg_expr =
data.args.get(i).expect("Could not get data argument");
let name = match arg_expr.deref() {
Node::Local(ref n) => Some(n),
_ => None,
}
.expect("Could not get argument");
let expr = v_args.get(i).expect("Could not get argument");
env.create_mutable_binding(
name.clone(),
false,
VariableScope::Function,
);
env.initialize_binding(name, expr.to_owned());
}
let result = self.run(&data.node);
self.realm.environment.pop();
result
}
},
match *(func_object.borrow()).deref() {
ValueData::Object(ref o) => (*o.deref().clone().borrow_mut())
.construct
.as_ref()
.unwrap()
.construct(&mut func_object.clone(), &v_args, self, &mut this),
_ => Ok(Gc::new(ValueData::Undefined)),
}
}
@ -542,11 +544,17 @@ impl Executor for Interpreter {
Ok(to_value(match *val {
ValueData::Undefined => "undefined",
ValueData::Symbol(_) => "symbol",
ValueData::Null | ValueData::Object(_) => "object",
ValueData::Null => "object",
ValueData::Boolean(_) => "boolean",
ValueData::Rational(_) | ValueData::Integer(_) => "number",
ValueData::String(_) => "string",
ValueData::Function(_) => "function",
ValueData::Object(ref o) => {
if o.deref().borrow().is_callable() {
"function"
} else {
"object"
}
}
}))
}
Node::StatementList(ref list) => {
@ -591,88 +599,25 @@ impl Interpreter {
}
/// https://tc39.es/ecma262/#sec-call
pub(crate) fn call(&mut self, f: &Value, v: &Value, arguments_list: Vec<Value>) -> ResultValue {
pub(crate) fn call(
&mut self,
f: &Value,
this: &mut Value,
arguments_list: &[Value],
) -> ResultValue {
// All functions should be objects, and eventually will be.
// During this transition call will support both native functions and function objects
match (*f).deref() {
ValueData::Object(ref obj) => {
let func: Value = obj.borrow_mut().deref_mut().get_internal_slot("call");
if !func.is_undefined() {
return self.call(&func, v, arguments_list);
}
// TODO: error object should be here
Err(Gc::new(ValueData::Undefined))
}
ValueData::Function(ref inner_func) => match *inner_func.deref().borrow() {
Function::NativeFunc(ref ntv) => {
let func = ntv.data;
func(v, &arguments_list, self)
}
Function::RegularFunc(ref data) => {
let env = &mut self.realm.environment;
// New target (second argument) is only needed for constructors, just pass undefined
let undefined = Gc::new(ValueData::Undefined);
env.push(new_function_environment(
f.clone(),
undefined,
Some(env.get_current_environment_ref().clone()),
));
for i in 0..data.args.len() {
let arg_expr = data.args.get(i).expect("Could not get data argument");
match arg_expr.deref() {
Node::Local(ref name) => {
let expr: &Value =
arguments_list.get(i).expect("Could not get argument");
self.realm.environment.create_mutable_binding(
name.clone(),
false,
VariableScope::Function,
);
self.realm
.environment
.initialize_binding(name, expr.clone());
}
Node::Spread(ref expr) => {
if let Node::Local(ref name) = expr.deref() {
let array = array::new_array(self)?;
array::add_to_array_object(&array, &arguments_list[i..])?;
self.realm.environment.create_mutable_binding(
name.clone(),
false,
VariableScope::Function,
);
self.realm.environment.initialize_binding(name, array);
} else {
panic!("Unsupported function argument declaration")
}
}
_ => panic!("Unsupported function argument declaration"),
}
}
// Add arguments object
let arguments_obj = create_unmapped_arguments_object(arguments_list);
self.realm.environment.create_mutable_binding(
"arguments".to_string(),
false,
VariableScope::Function,
);
self.realm
.environment
.initialize_binding("arguments", arguments_obj);
let result = self.run(&data.node);
self.realm.environment.pop();
result
}
ValueData::Object(ref obj) => match (*obj).deref().borrow().call {
Some(ref func) => func.call(&mut f.clone(), arguments_list, self, this),
None => panic!("Expected function"),
},
_ => Err(Gc::new(ValueData::Undefined)),
}
}
/// https://tc39.es/ecma262/#sec-ordinarytoprimitive
fn ordinary_to_primitive(&mut self, o: &Value, hint: &str) -> Value {
fn ordinary_to_primitive(&mut self, o: &mut Value, hint: &str) -> Value {
debug_assert!(o.get_type() == "object");
debug_assert!(hint == "string" || hint == "number");
let method_names: Vec<&str> = if hint == "string" {
@ -683,7 +628,7 @@ impl Interpreter {
for name in method_names.iter() {
let method: Value = o.get_field_slice(name);
if method.is_function() {
let result = self.call(&method, &o, vec![]);
let result = self.call(&method, o, &[]);
match result {
Ok(val) => {
if val.is_object() {
@ -704,7 +649,7 @@ impl Interpreter {
/// The abstract operation ToPrimitive takes an input argument and an optional argument PreferredType.
/// https://tc39.es/ecma262/#sec-toprimitive
#[allow(clippy::wrong_self_convention)]
pub fn to_primitive(&mut self, input: &Value, preferred_type: Option<&str>) -> Value {
pub fn to_primitive(&mut self, input: &mut Value, preferred_type: Option<&str>) -> Value {
let mut hint: &str;
match (*input).deref() {
ValueData::Object(_) => {
@ -723,7 +668,7 @@ impl Interpreter {
hint = "number";
};
self.ordinary_to_primitive(&input, hint)
self.ordinary_to_primitive(input, hint)
}
_ => input.clone(),
}
@ -740,7 +685,7 @@ impl Interpreter {
ValueData::Integer(ref num) => to_value(num.to_string()),
ValueData::String(ref string) => to_value(string.clone()),
ValueData::Object(_) => {
let prim_value = self.to_primitive(value, Some("string"));
let prim_value = self.to_primitive(&mut (value.clone()), Some("string"));
self.to_string(&prim_value)
}
_ => to_value("function(){...}"),
@ -750,7 +695,7 @@ impl Interpreter {
/// The abstract operation ToPropertyKey takes argument argument. It converts argument to a value that can be used as a property key.
/// https://tc39.es/ecma262/#sec-topropertykey
#[allow(clippy::wrong_self_convention)]
pub fn to_property_key(&mut self, value: &Value) -> Value {
pub fn to_property_key(&mut self, value: &mut Value) -> Value {
let key = self.to_primitive(value, Some("string"));
if key.is_symbol() {
key
@ -760,7 +705,7 @@ impl Interpreter {
}
/// https://tc39.es/ecma262/#sec-hasproperty
pub fn has_property(&self, obj: &Value, key: &Value) -> bool {
pub fn has_property(&self, obj: &mut Value, key: &Value) -> bool {
if let Some(obj) = obj.as_object() {
if !Property::is_property_key(key) {
false
@ -777,10 +722,9 @@ impl Interpreter {
#[allow(clippy::wrong_self_convention)]
pub fn to_object(&mut self, value: &Value) -> ResultValue {
match *value.deref().borrow() {
ValueData::Undefined
| ValueData::Function(_)
| ValueData::Integer(_)
| ValueData::Null => Err(Gc::new(ValueData::Undefined)),
ValueData::Undefined | ValueData::Integer(_) | ValueData::Null => {
Err(Gc::new(ValueData::Undefined))
}
ValueData::Boolean(_) => {
let proto = self
.realm
@ -825,7 +769,7 @@ impl Interpreter {
ValueData::Integer(ref num) => num.to_string(),
ValueData::String(ref string) => string.clone(),
ValueData::Object(_) => {
let prim_value = self.to_primitive(value, Some("string"));
let prim_value = self.to_primitive(&mut (value.clone()), Some("string"));
self.to_string(&prim_value).to_string()
}
_ => String::from("undefined"),
@ -846,7 +790,7 @@ impl Interpreter {
ValueData::Integer(num) => f64::from(num),
ValueData::String(ref string) => string.parse::<f64>().unwrap(),
ValueData::Object(_) => {
let prim_value = self.to_primitive(value, Some("number"));
let prim_value = self.to_primitive(&mut (value.clone()), Some("number"));
self.to_string(&prim_value)
.to_string()
.parse::<f64>()

2
boa/src/lib.rs

@ -21,7 +21,7 @@
rust_2018_compatibility,
rust_2018_idioms,
future_incompatible,
nonstandard_style
nonstandard_style,
)]
#![warn(clippy::perf, clippy::single_match_else, clippy::dbg_macro)]
#![allow(

10
boa/src/realm.rs

@ -21,6 +21,7 @@ use gc::{Gc, GcCell};
use std::collections::{hash_map::HashMap, hash_set::HashSet};
/// Representation of a Realm.
///
/// In the specification these are called Realm Records.
#[derive(Debug)]
pub struct Realm {
@ -54,14 +55,13 @@ impl Realm {
fn create_instrinsics(&self) {
let global = &self.global_obj;
// Create intrinsics, add global objects here
function::init(global);
global.set_field_slice("Array", array::create_constructor(global));
global.set_field_slice("Boolean", boolean::create_constructor(global));
global.set_field_slice("JSON", json::create_constructor(global));
global.set_field_slice("Math", math::create_constructor(global));
global.set_field_slice("Number", number::create_constructor(global));
global.set_field_slice("Object", object::create_constructor(global));
global.set_field_slice("Function", function::create_constructor(global));
global.set_field_slice("RegExp", regexp::create_constructor(global));
global.set_field_slice("String", string::create_constructor(global));
global.set_field_slice("Symbol", symbol::create_constructor(global));
@ -70,8 +70,12 @@ impl Realm {
/// Utility to add a function to the global object
pub fn register_global_func(self, func_name: &str, func: NativeFunctionData) -> Self {
let func = crate::builtins::function::Function::create_builtin(
vec![],
crate::builtins::function::FunctionBody::BuiltIn(func),
);
self.global_obj
.set_field(func_name.to_value(), func.to_value());
.set_field(func_name.to_value(), ValueData::from_func(func));
self
}

6
boa/src/syntax/ast/constant.rs

@ -103,19 +103,19 @@ pub enum Const {
impl From<&str> for Const {
fn from(s: &str) -> Self {
Const::String(s.into())
Self::String(s.into())
}
}
impl From<&String> for Const {
fn from(s: &String) -> Self {
Const::String(s.clone())
Self::String(s.clone())
}
}
impl From<String> for Const {
fn from(s: String) -> Self {
Const::String(s)
Self::String(s)
}
}

88
boa/src/syntax/ast/node.rs

@ -532,7 +532,7 @@ impl Node {
/// Creates an `ArrayDecl` AST node.
pub fn array_decl<N>(nodes: N) -> Self
where
N: Into<Vec<Node>>,
N: Into<Vec<Self>>,
{
Self::ArrayDecl(nodes.into())
}
@ -541,7 +541,7 @@ impl Node {
pub fn arrow_function_decl<P, B>(params: P, body: B) -> Self
where
P: Into<Vec<FormalParameter>>,
B: Into<Box<Node>>,
B: Into<Box<Self>>,
{
Self::ArrowFunctionDecl(params.into(), body.into())
}
@ -549,8 +549,8 @@ impl Node {
/// Creates an `Assign` AST node.
pub fn assign<L, R>(lhs: L, rhs: R) -> Self
where
L: Into<Box<Node>>,
R: Into<Box<Node>>,
L: Into<Box<Self>>,
R: Into<Box<Self>>,
{
Self::Assign(lhs.into(), rhs.into())
}
@ -559,8 +559,8 @@ impl Node {
pub fn bin_op<O, L, R>(op: O, lhs: L, rhs: R) -> Self
where
O: Into<BinOp>,
L: Into<Box<Node>>,
R: Into<Box<Node>>,
L: Into<Box<Self>>,
R: Into<Box<Self>>,
{
Self::BinOp(op.into(), lhs.into(), rhs.into())
}
@ -568,7 +568,7 @@ impl Node {
/// Creates a `Block` AST node.
pub fn block<N>(nodes: N) -> Self
where
N: Into<Vec<Node>>,
N: Into<Vec<Self>>,
{
Self::Block(nodes.into())
}
@ -585,8 +585,8 @@ impl Node {
/// Creates a `Call` AST node.
pub fn call<F, P>(function: F, params: P) -> Self
where
F: Into<Box<Node>>,
P: Into<Vec<Node>>,
F: Into<Box<Self>>,
P: Into<Vec<Self>>,
{
Self::Call(function.into(), params.into())
}
@ -594,9 +594,9 @@ impl Node {
/// Creates a `ConditionalOp` AST node.
pub fn conditional_op<C, T, F>(condition: C, if_true: T, if_false: F) -> Self
where
C: Into<Box<Node>>,
T: Into<Box<Node>>,
F: Into<Box<Node>>,
C: Into<Box<Self>>,
T: Into<Box<Self>>,
F: Into<Box<Self>>,
{
Self::ConditionalOp(condition.into(), if_true.into(), if_false.into())
}
@ -612,7 +612,7 @@ impl Node {
/// Creates a `ConstDecl` AST node.
pub fn const_decl<D>(decl: D) -> Self
where
D: Into<Vec<(String, Node)>>,
D: Into<Vec<(String, Self)>>,
{
Self::ConstDecl(decl.into())
}
@ -629,8 +629,8 @@ impl Node {
/// Creates a `DoWhileLoop` AST node.
pub fn do_while_loop<B, C>(body: B, condition: C) -> Self
where
B: Into<Box<Node>>,
C: Into<Box<Node>>,
B: Into<Box<Self>>,
C: Into<Box<Self>>,
{
Self::DoWhileLoop(body.into(), condition.into())
}
@ -641,7 +641,7 @@ impl Node {
N: Into<String>,
ON: Into<Option<N>>,
P: Into<Vec<FormalParameter>>,
B: Into<Box<Node>>,
B: Into<Box<Self>>,
{
Self::FunctionDecl(name.into().map(N::into), params.into(), body.into())
}
@ -649,7 +649,7 @@ impl Node {
/// Creates a `GetConstField` AST node.
pub fn get_const_field<V, L>(value: V, label: L) -> Self
where
V: Into<Box<Node>>,
V: Into<Box<Self>>,
L: Into<String>,
{
Self::GetConstField(value.into(), label.into())
@ -658,8 +658,8 @@ impl Node {
/// Creates a `GetField` AST node.
pub fn get_field<V, F>(value: V, field: F) -> Self
where
V: Into<Box<Node>>,
F: Into<Box<Node>>,
V: Into<Box<Self>>,
F: Into<Box<Self>>,
{
Self::GetField(value.into(), field.into())
}
@ -670,10 +670,10 @@ impl Node {
OI: Into<Option<I>>,
OC: Into<Option<C>>,
OS: Into<Option<S>>,
I: Into<Box<Node>>,
C: Into<Box<Node>>,
S: Into<Box<Node>>,
B: Into<Box<Node>>,
I: Into<Box<Self>>,
C: Into<Box<Self>>,
S: Into<Box<Self>>,
B: Into<Box<Self>>,
{
Self::ForLoop(
init.into().map(I::into),
@ -686,9 +686,9 @@ impl Node {
/// Creates an `If` AST node.
pub fn if_node<C, B, E, OE>(condition: C, body: B, else_node: OE) -> Self
where
C: Into<Box<Node>>,
B: Into<Box<Node>>,
E: Into<Box<Node>>,
C: Into<Box<Self>>,
B: Into<Box<Self>>,
E: Into<Box<Self>>,
OE: Into<Option<E>>,
{
Self::If(condition.into(), body.into(), else_node.into().map(E::into))
@ -697,7 +697,7 @@ impl Node {
/// Creates a `LetDecl` AST node.
pub fn let_decl<I>(init: I) -> Self
where
I: Into<Vec<(String, Option<Node>)>>,
I: Into<Vec<(String, Option<Self>)>>,
{
Self::LetDecl(init.into())
}
@ -713,7 +713,7 @@ impl Node {
/// Creates a `New` AST node.
pub fn new<N>(node: N) -> Self
where
N: Into<Box<Node>>,
N: Into<Box<Self>>,
{
Self::New(node.into())
}
@ -729,7 +729,7 @@ impl Node {
/// Creates a `Return` AST node.
pub fn return_node<E, OE>(expr: OE) -> Self
where
E: Into<Box<Node>>,
E: Into<Box<Self>>,
OE: Into<Option<E>>,
{
Self::Return(expr.into().map(E::into))
@ -738,10 +738,10 @@ impl Node {
/// Creates a `Switch` AST node.
pub fn switch<V, C, OD, D>(val: V, cases: C, default: OD) -> Self
where
V: Into<Box<Node>>,
C: Into<Vec<(Node, Vec<Node>)>>,
V: Into<Box<Self>>,
C: Into<Vec<(Self, Vec<Self>)>>,
OD: Into<Option<D>>,
D: Into<Box<Node>>,
D: Into<Box<Self>>,
{
Self::Switch(val.into(), cases.into(), default.into().map(D::into))
}
@ -749,7 +749,7 @@ impl Node {
/// Creates a `Spread` AST node.
pub fn spread<V>(val: V) -> Self
where
V: Into<Box<Node>>,
V: Into<Box<Self>>,
{
Self::Spread(val.into())
}
@ -757,7 +757,7 @@ impl Node {
/// Creates a `StatementList` AST node.
pub fn statement_list<L>(list: L) -> Self
where
L: Into<Vec<Node>>,
L: Into<Vec<Self>>,
{
Self::StatementList(list.into())
}
@ -765,7 +765,7 @@ impl Node {
/// Creates a `Throw` AST node.
pub fn throw<V>(val: V) -> Self
where
V: Into<Box<Node>>,
V: Into<Box<Self>>,
{
Self::Throw(val.into())
}
@ -773,7 +773,7 @@ impl Node {
/// Creates a `TypeOf` AST node.
pub fn type_of<E>(expr: E) -> Self
where
E: Into<Box<Node>>,
E: Into<Box<Self>>,
{
Self::TypeOf(expr.into())
}
@ -781,13 +781,13 @@ impl Node {
/// Creates a `Try` AST node.
pub fn try_node<T, OC, OP, OF, C, P, F>(try_node: T, catch: OC, param: OP, finally: OF) -> Self
where
T: Into<Box<Node>>,
T: Into<Box<Self>>,
OC: Into<Option<C>>,
OP: Into<Option<P>>,
OF: Into<Option<F>>,
C: Into<Box<Node>>,
P: Into<Box<Node>>,
F: Into<Box<Node>>,
C: Into<Box<Self>>,
P: Into<Box<Self>>,
F: Into<Box<Self>>,
{
let catch = catch.into().map(C::into);
let finally = finally.into().map(F::into);
@ -808,7 +808,7 @@ impl Node {
/// Creates a `UnaryOp` AST node.
pub fn unary_op<V>(op: UnaryOp, val: V) -> Self
where
V: Into<Box<Node>>,
V: Into<Box<Self>>,
{
Self::UnaryOp(op, val.into())
}
@ -816,7 +816,7 @@ impl Node {
/// Creates a `VarDecl` AST node.
pub fn var_decl<I>(init: I) -> Self
where
I: Into<Vec<(String, Option<Node>)>>,
I: Into<Vec<(String, Option<Self>)>>,
{
Self::VarDecl(init.into())
}
@ -824,8 +824,8 @@ impl Node {
/// Creates a `WhileLoop` AST node.
pub fn while_loop<C, B>(condition: C, body: B) -> Self
where
C: Into<Box<Node>>,
B: Into<Box<Node>>,
C: Into<Box<Self>>,
B: Into<Box<Self>>,
{
Self::WhileLoop(condition.into(), body.into())
}

5
boa/src/syntax/lexer/tests.rs

@ -420,7 +420,10 @@ fn hexadecimal_edge_case() {
TokenKind::Identifier(String::from("ff"))
);
assert_eq!(lexer.tokens[3].kind, TokenKind::numeric_literal(0xffffff));
assert_eq!(
lexer.tokens[3].kind,
TokenKind::numeric_literal(0x00ff_ffff)
);
}
#[test]

Loading…
Cancel
Save