Browse Source

Merge pull request #293 from HalidOdat/better-documentation

pull/304/head
HalidOdat 4 years ago committed by GitHub
parent
commit
41cda68da1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      README.md
  2. 233
      boa/src/builtins/array/mod.rs
  3. 35
      boa/src/builtins/boolean/mod.rs
  4. 130
      boa/src/builtins/console/mod.rs
  5. 32
      boa/src/builtins/error.rs
  6. 20
      boa/src/builtins/function/mod.rs
  7. 61
      boa/src/builtins/json/mod.rs
  8. 18
      boa/src/builtins/json/tests.rs
  9. 293
      boa/src/builtins/math/mod.rs
  10. 21
      boa/src/builtins/mod.rs
  11. 97
      boa/src/builtins/number/mod.rs
  12. 43
      boa/src/builtins/object/internal_methods_trait.rs
  13. 114
      boa/src/builtins/object/mod.rs
  14. 64
      boa/src/builtins/property.rs
  15. 170
      boa/src/builtins/regexp/mod.rs
  16. 365
      boa/src/builtins/string/mod.rs
  17. 49
      boa/src/builtins/symbol/mod.rs
  18. 30
      boa/src/builtins/value/mod.rs
  19. 2
      boa/src/environment/mod.rs
  20. 2
      boa/src/exec/mod.rs
  21. 5
      boa/src/lib.rs
  22. 94
      boa/src/syntax/ast/constant.rs
  23. 418
      boa/src/syntax/ast/keyword.rs
  24. 2
      boa/src/syntax/ast/mod.rs
  25. 580
      boa/src/syntax/ast/node.rs
  26. 689
      boa/src/syntax/ast/op.rs
  27. 2
      boa/src/syntax/ast/pos.rs
  28. 14
      boa/src/syntax/ast/punc.rs
  29. 51
      boa/src/syntax/ast/token.rs
  30. 44
      boa/src/syntax/lexer/mod.rs
  31. 5
      boa/src/syntax/mod.rs

4
README.md

@ -21,6 +21,10 @@ https://jasonwilliams.github.io/boa/
You can get more verbose errors when running from the command line You can get more verbose errors when running from the command line
## Development documentation
You can check the internal development docs at jasonwilliams.github.io/boa/doc
## Benchmarks ## Benchmarks
https://jasonwilliams.github.io/boa/dev/bench/ https://jasonwilliams.github.io/boa/dev/bench/

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

@ -1,3 +1,14 @@
//! This module implements the global `Array` object.
//!
//! The JavaScript `Array` class is a global object that is used in the construction of arrays; which are high-level, list-like objects.
//!
//! More information:
//! - [ECMAScript reference][spec]
//! - [MDN documentation][mdn]
//!
//! [spec]: https://tc39.es/ecma262/#sec-array-objects
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
@ -14,6 +25,7 @@ use gc::Gc;
use std::borrow::Borrow; use std::borrow::Borrow;
use std::cmp::{max, min}; use std::cmp::{max, min};
/// Creates a new `Array` instance.
pub(crate) fn new_array(interpreter: &Interpreter) -> ResultValue { pub(crate) fn new_array(interpreter: &Interpreter) -> ResultValue {
let array = ValueData::new_obj(Some( let array = ValueData::new_obj(Some(
&interpreter &interpreter
@ -36,8 +48,10 @@ pub(crate) fn new_array(interpreter: &Interpreter) -> ResultValue {
Ok(array) Ok(array)
} }
/// Utility function for creating array objects: `array_obj` can be any array with /// Utility function for creating array objects.
/// prototype already set (it will be wiped and recreated from `array_contents`) ///
/// `array_obj` can be any array with prototype already set (it will be wiped and
/// recreated from `array_contents`)
pub fn construct_array(array_obj: &Value, array_contents: &[Value]) -> ResultValue { pub fn construct_array(array_obj: &Value, array_contents: &[Value]) -> ResultValue {
let array_obj_ptr = array_obj.clone(); let array_obj_ptr = array_obj.clone();
@ -116,12 +130,17 @@ pub fn make_array(this: &Value, args: &[Value], ctx: &mut Interpreter) -> Result
Ok(this.clone()) Ok(this.clone())
} }
/// Array.isArray ( arg ) /// `Array.isArray( arg )`
/// ///
/// The isArray function takes one argument arg, and returns the Boolean value true /// The isArray function takes one argument arg, and returns the Boolean value true
/// if the argument is an object whose class internal property is "Array"; otherwise it returns false. /// if the argument is an object whose class internal property is "Array"; otherwise it returns false.
/// <https://tc39.es/ecma262/#sec-array.isarray> ///
/// ECMA-262 v5, 15.4.3.2 /// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, args: &[Value], _interpreter: &mut Interpreter) -> ResultValue {
let value_true = Gc::new(ValueData::Boolean(true)); let value_true = Gc::new(ValueData::Boolean(true));
let value_false = Gc::new(ValueData::Boolean(false)); let value_false = Gc::new(ValueData::Boolean(false));
@ -145,12 +164,18 @@ pub fn is_array(_this: &Value, args: &[Value], _interpreter: &mut Interpreter) -
} }
} }
/// Array.prototype.concat(...arguments) /// `Array.prototype.concat(...arguments)`
/// ///
/// When the concat method is called with zero or more arguments, it returns an /// When the concat method is called with zero or more arguments, it returns an
/// array containing the array elements of the object followed by the array /// array containing the array elements of the object followed by the array
/// elements of each argument in order. /// elements of each argument in order.
/// <https://tc39.es/ecma262/#sec-array.prototype.concat> ///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
if args.is_empty() { if args.is_empty() {
// If concat is called with no arguments, it returns the original array // If concat is called with no arguments, it returns the original array
@ -178,21 +203,33 @@ pub fn concat(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue
construct_array(this, &new_values) construct_array(this, &new_values)
} }
/// Array.prototype.push ( ...items ) /// `Array.prototype.push( ...items )`
/// ///
/// The arguments are appended to the end of the array, in the order in which /// The arguments are appended to the end of the array, in the order in which
/// they appear. The new length of the array is returned as the result of the /// they appear. The new length of the array is returned as the result of the
/// call. /// call.
/// <https://tc39.es/ecma262/#sec-array.prototype.push> ///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let new_array = add_to_array_object(this, args)?; let new_array = add_to_array_object(this, args)?;
Ok(new_array.get_field_slice("length")) Ok(new_array.get_field_slice("length"))
} }
/// Array.prototype.pop ( ) /// `Array.prototype.pop()`
/// ///
/// The last element of the array is removed from the array and returned. /// The last element of the array is removed from the array and returned.
/// <https://tc39.es/ecma262/#sec-array.prototype.pop> ///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let curr_length: i32 = let curr_length: i32 =
from_value(this.get_field_slice("length")).expect("Could not convert argument to i32"); from_value(this.get_field_slice("length")).expect("Could not convert argument to i32");
@ -206,10 +243,16 @@ pub fn pop(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(pop_value) Ok(pop_value)
} }
/// Array.prototype.forEach ( callbackFn [ , thisArg ] ) /// `Array.prototype.forEach( callbackFn [ , thisArg ] )`
/// ///
/// This method executes the provided callback function for each element in the array. /// This method executes the provided callback function for each element in the array.
/// <https://tc39.es/ecma262/#sec-array.prototype.foreach> ///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
if args.is_empty() { if args.is_empty() {
return Err(to_value( return Err(to_value(
@ -233,12 +276,18 @@ pub fn for_each(this: &Value, args: &[Value], interpreter: &mut Interpreter) ->
Ok(Gc::new(ValueData::Undefined)) Ok(Gc::new(ValueData::Undefined))
} }
/// Array.prototype.join ( separator ) /// `Array.prototype.join( separator )`
/// ///
/// The elements of the array are converted to Strings, and these Strings are /// The elements of the array are converted to Strings, and these Strings are
/// then concatenated, separated by occurrences of the separator. If no /// then concatenated, separated by occurrences of the separator. If no
/// separator is provided, a single comma is used as the separator. /// separator is provided, a single comma is used as the separator.
/// <https://tc39.es/ecma262/#sec-array.prototype.join> ///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let separator = if args.is_empty() { let separator = if args.is_empty() {
String::from(",") String::from(",")
@ -257,12 +306,18 @@ pub fn join(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(elem_strs.join(&separator))) Ok(to_value(elem_strs.join(&separator)))
} }
/// Array.prototype.toString ( separator ) /// `Array.prototype.toString( separator )`
/// ///
/// The toString function is intentionally generic; it does not require that /// The toString function is intentionally generic; it does not require that
/// its this value be an Array object. Therefore it can be transferred to /// its this value be an Array object. Therefore it can be transferred to
/// other kinds of objects for use as a method. /// other kinds of objects for use as a method.
/// <https://tc39.es/ecma262/#sec-array.prototype.tostring> ///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
let method_name = "join"; let method_name = "join";
let mut arguments = vec![to_value(",")]; let mut arguments = vec![to_value(",")];
@ -293,11 +348,17 @@ pub fn to_string(this: &Value, _args: &[Value], _ctx: &mut Interpreter) -> Resul
Ok(to_value(match_string)) Ok(to_value(match_string))
} }
/// Array.prototype.reverse ( ) /// `Array.prototype.reverse()`
/// ///
/// The elements of the array are rearranged so as to reverse their order. /// The elements of the array are rearranged so as to reverse their order.
/// The object is returned as the result of the call. /// The object is returned as the result of the call.
/// <https://tc39.es/ecma262/#sec-array.prototype.reverse/> ///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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)] #[allow(clippy::else_if_without_else)]
pub fn reverse(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { pub fn reverse(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let len: i32 = let len: i32 =
@ -328,10 +389,16 @@ pub fn reverse(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(this.clone()) Ok(this.clone())
} }
/// Array.prototype.shift ( ) /// `Array.prototype.shift()`
/// ///
/// The first element of the array is removed from the array and returned. /// The first element of the array is removed from the array and returned.
/// <https://tc39.es/ecma262/#sec-array.prototype.shift/> ///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let len: i32 = let len: i32 =
from_value(this.get_field_slice("length")).expect("Could not convert argument to i32"); from_value(this.get_field_slice("length")).expect("Could not convert argument to i32");
@ -363,12 +430,18 @@ pub fn shift(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(first) Ok(first)
} }
/// Array.prototype.unshift ( ...items ) /// `Array.prototype.unshift( ...items )`
/// ///
/// The arguments are prepended to the start of the array, such that their order /// The arguments are prepended to the start of the array, such that their order
/// within the array is the same as the order in which they appear in the /// within the array is the same as the order in which they appear in the
/// argument list. /// argument list.
/// <https://tc39.es/ecma262/#sec-array.prototype.unshift/> ///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let len: i32 = let len: i32 =
from_value(this.get_field_slice("length")).expect("Could not convert argument to i32"); from_value(this.get_field_slice("length")).expect("Could not convert argument to i32");
@ -401,13 +474,19 @@ pub fn unshift(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue
Ok(to_value(temp)) Ok(to_value(temp))
} }
/// Array.prototype.every ( callback, [ thisArg ] ) /// `Array.prototype.every( callback, [ thisArg ] )`
/// ///
/// The every method executes the provided callback function once for each /// The every method executes the provided callback function once for each
/// element present in the array until it finds the one where callback returns /// element present in the array until it finds the one where callback returns
/// a falsy value. It returns `false` if it finds such element, otherwise it /// a falsy value. It returns `false` if it finds such element, otherwise it
/// returns `true`. /// returns `true`.
/// <https://tc39.es/ecma262/#sec-array.prototype.every/> ///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
if args.is_empty() { if args.is_empty() {
return Err(to_value( return Err(to_value(
@ -436,11 +515,17 @@ pub fn every(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Res
Ok(to_value(true)) Ok(to_value(true))
} }
/// Array.prototype.map ( callback, [ thisArg ] ) /// `Array.prototype.map( callback, [ thisArg ] )`
/// ///
/// For each element in the array the callback function is called, and a new /// For each element in the array the callback function is called, and a new
/// array is constructed from the return values of these calls. /// array is constructed from the return values of these calls.
/// <https://tc39.es/ecma262/#sec-array.prototype.map> ///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
if args.is_empty() { if args.is_empty() {
return Err(to_value( return Err(to_value(
@ -471,7 +556,7 @@ pub fn map(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Resul
construct_array(&new, &values) construct_array(&new, &values)
} }
/// Array.prototype.indexOf ( searchElement[, fromIndex ] ) /// `Array.prototype.indexOf( searchElement[, fromIndex ] )`
/// ///
/// ///
/// indexOf compares searchElement to the elements of the array, in ascending order, /// indexOf compares searchElement to the elements of the array, in ascending order,
@ -483,7 +568,13 @@ pub fn map(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Resul
/// i.e. the array will not be searched. If it is negative, it is used as the offset /// i.e. the array will not be searched. If it is negative, it is used as the offset
/// from the end of the array to compute fromIndex. If the computed index is less than 0, /// from the end of the array to compute fromIndex. If the computed index is less than 0,
/// the whole array will be searched. /// the whole array will be searched.
/// <https://tc39.es/ecma262/#sec-array.prototype.indexof> ///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
// If no arguments, return -1. Not described in spec, but is what chrome does. // If no arguments, return -1. Not described in spec, but is what chrome does.
if args.is_empty() { if args.is_empty() {
@ -521,7 +612,7 @@ pub fn index_of(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValu
Ok(to_value(-1)) Ok(to_value(-1))
} }
/// Array.prototype.lastIndexOf ( searchElement[, fromIndex ] ) /// `Array.prototype.lastIndexOf( searchElement[, fromIndex ] )`
/// ///
/// ///
/// lastIndexOf compares searchElement to the elements of the array in descending order /// lastIndexOf compares searchElement to the elements of the array in descending order
@ -532,7 +623,13 @@ pub fn index_of(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValu
/// (i.e. the whole array is searched). If it is greater than or equal to the length of the array, /// (i.e. the whole array is searched). If it is greater than or equal to the length of the array,
/// the whole array will be searched. If it is negative, it is used as the offset from the end /// the whole array will be searched. If it is negative, it is used as the offset from the end
/// of the array to compute fromIndex. If the computed index is less than 0, -1 is returned. /// of the array to compute fromIndex. If the computed index is less than 0, -1 is returned.
/// <https://tc39.es/ecma262/#sec-array.prototype.lastindexof> ///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
// If no arguments, return -1. Not described in spec, but is what chrome does. // If no arguments, return -1. Not described in spec, but is what chrome does.
if args.is_empty() { if args.is_empty() {
@ -570,12 +667,18 @@ pub fn last_index_of(this: &Value, args: &[Value], _: &mut Interpreter) -> Resul
Ok(to_value(-1)) Ok(to_value(-1))
} }
/// Array.prototype.find ( callback, [thisArg] ) /// `Array.prototype.find( callback, [thisArg] )`
/// ///
/// The find method executes the callback function once for each index of the array /// The find method executes the callback function once for each index of the array
/// until the callback returns a truthy value. If so, find immediately returns the value /// until the callback returns a truthy value. If so, find immediately returns the value
/// of that element. Otherwise, find returns undefined. /// of that element. Otherwise, find returns undefined.
/// <https://tc39.es/ecma262/#sec-array.prototype.find> ///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
if args.is_empty() { if args.is_empty() {
return Err(to_value( return Err(to_value(
@ -600,12 +703,18 @@ pub fn find(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Resu
Ok(Gc::new(ValueData::Undefined)) Ok(Gc::new(ValueData::Undefined))
} }
/// Array.prototype.findIndex ( predicate [ , thisArg ] ) /// `Array.prototype.findIndex( predicate [ , thisArg ] )`
/// ///
/// This method executes the provided predicate function for each element of the array. /// This method executes the provided predicate function for each element of the array.
/// If the predicate function returns `true` for an element, this method returns the index of the element. /// If the predicate function returns `true` for an element, this method returns the index of the element.
/// If all elements return `false`, the value `-1` is returned. /// If all elements return `false`, the value `-1` is returned.
/// <https://tc39.es/ecma262/#sec-array.prototype.findindex/> ///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
if args.is_empty() { if args.is_empty() {
return Err(to_value( return Err(to_value(
@ -637,11 +746,17 @@ pub fn find_index(this: &Value, args: &[Value], interpreter: &mut Interpreter) -
Ok(Gc::new(ValueData::Number(f64::from(-1)))) Ok(Gc::new(ValueData::Number(f64::from(-1))))
} }
/// Array.prototype.fill ( value[, start[, end]] ) /// `Array.prototype.fill( value[, start[, end]] )`
/// ///
/// The method fills (modifies) all the elements of an array from start index (default 0) /// The method fills (modifies) all the elements of an array from start index (default 0)
/// to an end index (default array length) with a static value. It returns the modified array /// to an end index (default array length) with a static value. It returns the modified array.
/// <https://tc39.es/ecma262/#sec-array.prototype.fill> ///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let len: i32 = from_value(this.get_field_slice("length")).expect("Could not get argument"); let len: i32 = from_value(this.get_field_slice("length")).expect("Could not get argument");
let default_value = undefined(); let default_value = undefined();
@ -671,6 +786,16 @@ pub fn fill(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(this.clone()) Ok(this.clone())
} }
/// `Array.prototype.includes( valueToFind [, fromIndex] )`
///
/// Determines whether an array includes a certain value among its entries, returning `true` or `false` as appropriate.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let search_element = args let search_element = args
.get(0) .get(0)
@ -691,14 +816,20 @@ pub fn includes_value(this: &Value, args: &[Value], _: &mut Interpreter) -> Resu
Ok(to_value(false)) Ok(to_value(false))
} }
/// Array.prototype.slice ( [begin[, end]] ) /// `Array.prototype.slice( [begin[, end]] )`
/// ///
/// The slice method takes two arguments, start and end, and returns an array containing the /// The slice method takes two arguments, start and end, and returns an array containing the
/// elements of the array from element start up to, but not including, element end (or through the /// elements of the array from element start up to, but not including, element end (or through the
/// end of the array if end is undefined). If start is negative, it is treated as length + start /// end of the array if end is undefined). If start is negative, it is treated as length + start
/// where length is the length of the array. If end is negative, it is treated as length + end where /// where length is the length of the array. If end is negative, it is treated as length + end where
/// length is the length of the array. /// length is the length of the array.
/// <https://tc39.es/ecma262/#sec-array.prototype.slice> ///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
let new_array = new_array(interpreter)?; let new_array = new_array(interpreter)?;
let len: i32 = let len: i32 =
@ -737,11 +868,17 @@ pub fn slice(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Res
Ok(new_array) Ok(new_array)
} }
/// Array.prototype.filter ( callback, [ thisArg ] ) /// `Array.prototype.filter( callback, [ thisArg ] )`
/// ///
/// For each element in the array the callback function is called, and a new /// For each element in the array the callback function is called, and a new
/// array is constructed for every value whose callback returned a truthy value /// array is constructed for every value whose callback returned a truthy value.
/// <https://tc39.es/ecma262/#sec-array.prototype.filter> ///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
if args.is_empty() { if args.is_empty() {
return Err(to_value( return Err(to_value(
@ -786,7 +923,13 @@ pub fn filter(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Re
/// in the array. Otherwise, false. /// in the array. Otherwise, false.
/// ///
/// Caution: Calling this method on an empty array returns false for any condition! /// Caution: Calling this method on an empty array returns false for any condition!
/// <https://tc39.es/ecma262/#sec-array.prototype.some/> ///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
if args.is_empty() { if args.is_empty() {
return Err(to_value( return Err(to_value(
@ -816,7 +959,7 @@ pub fn some(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Resu
Ok(to_value(false)) Ok(to_value(false))
} }
/// Create a new `Array` object /// Create a new `Array` object.
pub fn create_constructor(global: &Value) -> Value { pub fn create_constructor(global: &Value) -> Value {
// Create Constructor // Create Constructor
let object_prototype = global.get_field_slice("Object").get_field_slice(PROTOTYPE); let object_prototype = global.get_field_slice("Object").get_field_slice(PROTOTYPE);

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

@ -1,3 +1,14 @@
//! This module implements the global `Boolean` object.
//!
//! The `Boolean` object is an object wrapper for a boolean value.
//!
//! More information:
//! - [ECMAScript reference][spec]
//! - [MDN documentation][mdn]
//!
//! [spec]: https://tc39.es/ecma262/#sec-boolean-object
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
@ -35,13 +46,27 @@ pub fn call_boolean(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultVal
} }
} }
/// https://tc39.es/ecma262/#sec-boolean.prototype.tostring /// The `toString()` method returns a string representing the specified `Boolean` object.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let b = this_boolean_value(this); let b = this_boolean_value(this);
Ok(to_value(b.to_string())) Ok(to_value(b.to_string()))
} }
/// https://tc39.es/ecma262/#sec-boolean.prototype.valueof /// The valueOf() method returns the primitive value of a `Boolean` object.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(this_boolean_value(this)) Ok(this_boolean_value(this))
} }
@ -79,6 +104,12 @@ pub fn to_boolean(value: &Value) -> Value {
} }
} }
/// An Utility function used to get the internal BooleanData.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-thisbooleanvalue
pub fn this_boolean_value(value: &Value) -> Value { pub fn this_boolean_value(value: &Value) -> Value {
match *value.deref().borrow() { match *value.deref().borrow() {
ValueData::Boolean(v) => to_value(v), ValueData::Boolean(v) => to_value(v),

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

@ -1,3 +1,16 @@
//! This module implements the global `console` object.
//!
//! The `console` object can be accessed from any global object.
//!
//! The specifics of how it works varies from browser to browser, but there is a de facto set of features that are typically provided.
//!
//! More information:
//! - [MDN documentation][mdn]
//! - [WHATWG `console` specification][spec]
//!
//! [spec]: https://console.spec.whatwg.org/
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/Console
#![allow(clippy::print_stdout)] #![allow(clippy::print_stdout)]
#[cfg(test)] #[cfg(test)]
@ -14,6 +27,7 @@ use crate::{
use gc::Gc; use gc::Gc;
use std::{collections::HashMap, time::SystemTime}; use std::{collections::HashMap, time::SystemTime};
/// This is the internal console object state.
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct ConsoleState { pub struct ConsoleState {
count_map: HashMap<String, u32>, count_map: HashMap<String, u32>,
@ -33,6 +47,7 @@ impl ConsoleState {
impl InternalState for ConsoleState {} impl InternalState for ConsoleState {}
/// This represents the different types of log messages.
#[derive(Debug)] #[derive(Debug)]
pub enum LogMessage { pub enum LogMessage {
Log(String), Log(String),
@ -41,12 +56,14 @@ pub enum LogMessage {
Error(String), Error(String),
} }
/// Helper function that returns the argument at a specified index.
fn get_arg_at_index<T: FromValue + Default>(args: &[Value], index: usize) -> Option<T> { fn get_arg_at_index<T: FromValue + Default>(args: &[Value], index: usize) -> Option<T> {
args.get(index) args.get(index)
.cloned() .cloned()
.map(|s| from_value::<T>(s).expect("Convert error")) .map(|s| from_value::<T>(s).expect("Convert error"))
} }
/// Helper function for logging messages.
pub fn logger(msg: LogMessage, console_state: &ConsoleState) { pub fn logger(msg: LogMessage, console_state: &ConsoleState) {
let indent = 2 * console_state.groups.len(); let indent = 2 * console_state.groups.len();
@ -60,6 +77,7 @@ pub fn logger(msg: LogMessage, console_state: &ConsoleState) {
} }
} }
/// This represents the `console` formatter.
pub fn formatter(data: &[Value]) -> String { pub fn formatter(data: &[Value]) -> String {
let target = get_arg_at_index::<String>(data, 0).unwrap_or_default(); let target = get_arg_at_index::<String>(data, 0).unwrap_or_default();
match data.len() { match data.len() {
@ -125,7 +143,12 @@ pub fn formatter(data: &[Value]) -> String {
/// Prints a JavaScript value to the standard error if first argument evaluates to `false` or there /// Prints a JavaScript value to the standard error if first argument evaluates to `false` or there
/// were no arguments. /// were no arguments.
/// ///
/// More information: <https://console.spec.whatwg.org/#assert> /// More information:
/// - [MDN documentation][mdn]
/// - [WHATWG `console` specification][spec]
///
/// [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: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let assertion = get_arg_at_index::<bool>(args, 0).unwrap_or_default(); let assertion = get_arg_at_index::<bool>(args, 0).unwrap_or_default();
@ -153,7 +176,12 @@ pub fn assert(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue
/// ///
/// Removes all groups and clears console if possible. /// Removes all groups and clears console if possible.
/// ///
/// More information: <https://console.spec.whatwg.org/#clear> /// More information:
/// - [MDN documentation][mdn]
/// - [WHATWG `console` specification][spec]
///
/// [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: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_mut(|state: &mut ConsoleState| { this.with_internal_state_mut(|state: &mut ConsoleState| {
state.groups.clear(); state.groups.clear();
@ -166,7 +194,12 @@ pub fn clear(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
/// ///
/// Prints a JavaScript values with "debug" logLevel. /// Prints a JavaScript values with "debug" logLevel.
/// ///
/// More information: <https://console.spec.whatwg.org/#debug> /// More information:
/// - [MDN documentation][mdn]
/// - [WHATWG `console` specification][spec]
///
/// [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: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|state| logger(LogMessage::Log(formatter(&args[..])), state)); this.with_internal_state_ref(|state| logger(LogMessage::Log(formatter(&args[..])), state));
Ok(Gc::new(ValueData::Undefined)) Ok(Gc::new(ValueData::Undefined))
@ -176,7 +209,12 @@ pub fn debug(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// ///
/// Prints a JavaScript values with "error" logLevel. /// Prints a JavaScript values with "error" logLevel.
/// ///
/// More information: <https://console.spec.whatwg.org/#error> /// More information:
/// - [MDN documentation][mdn]
/// - [WHATWG `console` specification][spec]
///
/// [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: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|state| logger(LogMessage::Error(formatter(&args[..])), state)); this.with_internal_state_ref(|state| logger(LogMessage::Error(formatter(&args[..])), state));
Ok(Gc::new(ValueData::Undefined)) Ok(Gc::new(ValueData::Undefined))
@ -186,7 +224,12 @@ pub fn error(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// ///
/// Prints a JavaScript values with "info" logLevel. /// Prints a JavaScript values with "info" logLevel.
/// ///
/// More information: <https://console.spec.whatwg.org/#info> /// More information:
/// - [MDN documentation][mdn]
/// - [WHATWG `console` specification][spec]
///
/// [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: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|state| logger(LogMessage::Info(formatter(&args[..])), state)); this.with_internal_state_ref(|state| logger(LogMessage::Info(formatter(&args[..])), state));
Ok(Gc::new(ValueData::Undefined)) Ok(Gc::new(ValueData::Undefined))
@ -196,7 +239,12 @@ pub fn info(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// ///
/// Prints a JavaScript values with "log" logLevel. /// Prints a JavaScript values with "log" logLevel.
/// ///
/// More information: <https://console.spec.whatwg.org/#log> /// More information:
/// - [MDN documentation][mdn]
/// - [WHATWG `console` specification][spec]
///
/// [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: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|state| logger(LogMessage::Log(formatter(&args[..])), state)); this.with_internal_state_ref(|state| logger(LogMessage::Log(formatter(&args[..])), state));
Ok(Gc::new(ValueData::Undefined)) Ok(Gc::new(ValueData::Undefined))
@ -206,7 +254,12 @@ pub fn log(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// ///
/// Prints a stack trace with "trace" logLevel, optionally labelled by data. /// Prints a stack trace with "trace" logLevel, optionally labelled by data.
/// ///
/// More information: <https://console.spec.whatwg.org/#trace> /// More information:
/// - [MDN documentation][mdn]
/// - [WHATWG `console` specification][spec]
///
/// [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: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
if !args.is_empty() { if !args.is_empty() {
this.with_internal_state_ref(|state| logger(LogMessage::Log(formatter(&args[..])), state)); this.with_internal_state_ref(|state| logger(LogMessage::Log(formatter(&args[..])), state));
@ -227,7 +280,12 @@ pub fn trace(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// ///
/// Prints a JavaScript values with "warn" logLevel. /// Prints a JavaScript values with "warn" logLevel.
/// ///
/// More information: <https://console.spec.whatwg.org/#warn> /// More information:
/// - [MDN documentation][mdn]
/// - [WHATWG `console` specification][spec]
///
/// [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: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|state| logger(LogMessage::Warn(formatter(&args[..])), state)); this.with_internal_state_ref(|state| logger(LogMessage::Warn(formatter(&args[..])), state));
Ok(Gc::new(ValueData::Undefined)) Ok(Gc::new(ValueData::Undefined))
@ -237,7 +295,12 @@ pub fn warn(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// ///
/// Prints number of times the function was called with that particular label. /// Prints number of times the function was called with that particular label.
/// ///
/// More information: <https://console.spec.whatwg.org/#count> /// More information:
/// - [MDN documentation][mdn]
/// - [WHATWG `console` specification][spec]
///
/// [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: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let label = get_arg_at_index::<String>(args, 0).unwrap_or_else(|| "default".to_string()); let label = get_arg_at_index::<String>(args, 0).unwrap_or_else(|| "default".to_string());
@ -256,7 +319,12 @@ pub fn count(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// ///
/// Resets the counter for label. /// Resets the counter for label.
/// ///
/// More information: <https://console.spec.whatwg.org/#countreset> /// More information:
/// - [MDN documentation][mdn]
/// - [WHATWG `console` specification][spec]
///
/// [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: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let label = get_arg_at_index::<String>(args, 0).unwrap_or_else(|| "default".to_string()); let label = get_arg_at_index::<String>(args, 0).unwrap_or_else(|| "default".to_string());
@ -281,7 +349,12 @@ fn system_time_in_ms() -> u128 {
/// ///
/// Starts the timer for given label. /// Starts the timer for given label.
/// ///
/// More information: <https://console.spec.whatwg.org/#time> /// More information:
/// - [MDN documentation][mdn]
/// - [WHATWG `console` specification][spec]
///
/// [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: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let label = get_arg_at_index::<String>(args, 0).unwrap_or_else(|| "default".to_string()); let label = get_arg_at_index::<String>(args, 0).unwrap_or_else(|| "default".to_string());
@ -304,7 +377,12 @@ pub fn time(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// ///
/// Prints elapsed time for timer with given label. /// Prints elapsed time for timer with given label.
/// ///
/// More information: <https://console.spec.whatwg.org/#timelog> /// More information:
/// - [MDN documentation][mdn]
/// - [WHATWG `console` specification][spec]
///
/// [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: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let label = get_arg_at_index::<String>(args, 0).unwrap_or_else(|| "default".to_string()); let label = get_arg_at_index::<String>(args, 0).unwrap_or_else(|| "default".to_string());
@ -331,7 +409,12 @@ pub fn time_log(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValu
/// ///
/// Removes the timer with given label. /// Removes the timer with given label.
/// ///
/// More information: <https://console.spec.whatwg.org/#timeend> /// More information:
/// - [MDN documentation][mdn]
/// - [WHATWG `console` specification][spec]
///
/// [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: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let label = get_arg_at_index::<String>(args, 0).unwrap_or_else(|| "default".to_string()); let label = get_arg_at_index::<String>(args, 0).unwrap_or_else(|| "default".to_string());
@ -357,7 +440,12 @@ pub fn time_end(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValu
/// ///
/// Adds new group with name from formatted data to stack. /// Adds new group with name from formatted data to stack.
/// ///
/// More information: <https://console.spec.whatwg.org/#group> /// More information:
/// - [MDN documentation][mdn]
/// - [WHATWG `console` specification][spec]
///
/// [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: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let group_label = formatter(args); let group_label = formatter(args);
@ -373,7 +461,12 @@ pub fn group(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// ///
/// Removes the last group from the stack. /// Removes the last group from the stack.
/// ///
/// More information: <https://console.spec.whatwg.org/#groupend> /// More information:
/// - [MDN documentation][mdn]
/// - [WHATWG `console` specification][spec]
///
/// [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: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_mut(|state: &mut ConsoleState| { this.with_internal_state_mut(|state: &mut ConsoleState| {
state.groups.pop(); state.groups.pop();
@ -386,7 +479,12 @@ pub fn group_end(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue
/// ///
/// Prints info about item /// Prints info about item
/// ///
/// More information: <https://console.spec.whatwg.org/#dir> /// More information:
/// - [MDN documentation][mdn]
/// - [WHATWG `console` specification][spec]
///
/// [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: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_mut(|state: &mut ConsoleState| { this.with_internal_state_mut(|state: &mut ConsoleState| {
logger( logger(

32
boa/src/builtins/error.rs

@ -1,3 +1,15 @@
//! This module implements the global `Error` object.
//!
//! Error objects are thrown when runtime errors occur.
//! The Error object can also be used as a base object for user-defined exceptions.
//!
//! More information:
//! - [MDN documentation][mdn]
//! - [ECMAScript reference][spec]
//!
//! [spec]: https://tc39.es/ecma262/#sec-error-objects
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
use crate::{ use crate::{
builtins::{ builtins::{
function::NativeFunctionData, function::NativeFunctionData,
@ -8,7 +20,7 @@ use crate::{
}; };
use gc::Gc; use gc::Gc;
/// Create a new error /// Create a new error object.
pub fn make_error(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub fn make_error(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
if !args.is_empty() { if !args.is_empty() {
this.set_field_slice( this.set_field_slice(
@ -25,13 +37,24 @@ pub fn make_error(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultVa
this.set_kind(ObjectKind::Error); this.set_kind(ObjectKind::Error);
Ok(Gc::new(ValueData::Undefined)) Ok(Gc::new(ValueData::Undefined))
} }
/// Get the string representation of the error
/// `Error.prototype.toString()`
///
/// The toString() method returns a string representing the specified Error object.
///
/// More information:
/// - [MDN documentation][mdn]
/// - [ECMAScript reference][spec]
///
/// [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: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let name = this.get_field_slice("name"); let name = this.get_field_slice("name");
let message = this.get_field_slice("message"); let message = this.get_field_slice("message");
Ok(to_value(format!("{}: {}", name, message))) Ok(to_value(format!("{}: {}", name, message)))
} }
/// Create a new `Error` object
/// Create a new `Error` object.
pub fn _create(global: &Value) -> Value { pub fn _create(global: &Value) -> Value {
let prototype = ValueData::new_obj(Some(global)); let prototype = ValueData::new_obj(Some(global));
prototype.set_field_slice("message", to_value("")); prototype.set_field_slice("message", to_value(""));
@ -41,7 +64,8 @@ pub fn _create(global: &Value) -> Value {
error.set_field_slice(PROTOTYPE, prototype); error.set_field_slice(PROTOTYPE, prototype);
error error
} }
/// Initialise the global object with the `Error` object
/// Initialise the global object with the `Error` object.
pub fn init(global: &Value) { pub fn init(global: &Value) {
global.set_field_slice("Error", _create(global)); global.set_field_slice("Error", _create(global));
} }

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

@ -1,3 +1,14 @@
//! This module implements the global `Function` object.
//!
//! `Every JavaScript `function` is actually a `Function` object.
//!
//! More information:
//! - [ECMAScript reference][spec]
//! - [MDN documentation][mdn]
//!
//! [spec]: https://tc39.es/ecma262/#sec-function-object
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
@ -18,12 +29,11 @@ use std::ops::Deref;
/// fn(this, arguments, ctx) /// fn(this, arguments, ctx)
pub type NativeFunctionData = fn(&Value, &[Value], &mut Interpreter) -> ResultValue; pub type NativeFunctionData = fn(&Value, &[Value], &mut Interpreter) -> ResultValue;
/// A Javascript function /// A Javascript `Function` object instance.
///
/// A member of the Object type that may be invoked as a subroutine /// A member of the Object type that may be invoked as a subroutine
/// <https://tc39.es/ecma262/#sec-terms-and-definitions-function> ///
/// In our implementation, Function is extending Object by holding an object field which some extra data /// In our implementation, Function is extending Object by holding an object field which some extra data
/// A Javascript function
#[derive(Trace, Finalize, Debug, Clone)] #[derive(Trace, Finalize, Debug, Clone)]
pub enum Function { pub enum Function {
/// A native javascript function /// A native javascript function
@ -32,7 +42,7 @@ pub enum Function {
RegularFunc(RegularFunction), RegularFunc(RegularFunction),
} }
/// Represents a regular javascript function in memory /// Represents a regular javascript function in memory.
#[derive(Trace, Finalize, Debug, Clone)] #[derive(Trace, Finalize, Debug, Clone)]
pub struct RegularFunction { pub struct RegularFunction {
/// The fields associated with the function /// The fields associated with the function

61
boa/src/builtins/json.rs → boa/src/builtins/json/mod.rs

@ -1,6 +1,6 @@
//! This module implements the global `JSON` object. //! This module implements the global `JSON` object.
//! //!
//! The `JSON` object contains methods for parsing [JavaScript Object Notation (JSON)][json] //! The `JSON` object contains methods for parsing [JavaScript Object Notation (JSON)][spec]
//! and converting values to JSON. It can't be called or constructed, and aside from its //! and converting values to JSON. It can't be called or constructed, and aside from its
//! two method properties, it has no interesting functionality of its own. //! two method properties, it has no interesting functionality of its own.
//! //!
@ -18,8 +18,22 @@ use crate::builtins::value::{to_value, ResultValue, Value, ValueData};
use crate::exec::Interpreter; use crate::exec::Interpreter;
use serde_json::{self, Value as JSONValue}; use serde_json::{self, Value as JSONValue};
/// Parse a JSON string into a Javascript object #[cfg(test)]
/// <https://tc39.es/ecma262/#sec-json.parse> mod tests;
/// `JSON.parse( text[, reviver] )`
///
/// This `JSON` method parses a JSON string, constructing the JavaScript value or object described by the string.
///
/// An optional `reviver` function can be provided to perform a transformation on the resulting object before it is returned.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
match serde_json::from_str::<JSONValue>( match serde_json::from_str::<JSONValue>(
&args &args
@ -33,14 +47,29 @@ pub fn parse(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
} }
} }
/// Process a Javascript object into a JSON string /// `JSON.stringify( value[, replacer[, space]] )`
///
/// This `JSON` method converts a JavaScript object or value to a JSON string.
///
/// This medhod optionally replaces values if a `replacer` function is specified or
/// optionally including only the specified properties if a replacer array is specified.
///
/// An optional `space` argument can be supplied of type `String` or `Number` that's used to insert
/// white space into the output JSON string for readability purposes.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let obj = args.get(0).expect("cannot get argument for JSON.stringify"); let obj = args.get(0).expect("cannot get argument for JSON.stringify");
let json = obj.to_json().to_string(); let json = obj.to_json().to_string();
Ok(to_value(json)) Ok(to_value(json))
} }
/// Create a new `JSON` object /// Create a new `JSON` object.
pub fn create_constructor(global: &Value) -> Value { pub fn create_constructor(global: &Value) -> Value {
let json = ValueData::new_obj(Some(global)); let json = ValueData::new_obj(Some(global));
@ -49,25 +78,3 @@ pub fn create_constructor(global: &Value) -> Value {
to_value(json) to_value(json)
} }
#[cfg(test)]
mod tests {
use crate::{exec::Executor, forward, realm::Realm};
#[test]
fn json_sanity() {
let realm = Realm::create();
let mut engine = Executor::new(realm);
assert_eq!(
forward(&mut engine, r#"JSON.parse('{"aaa":"bbb"}').aaa == 'bbb'"#),
"true"
);
assert_eq!(
forward(
&mut engine,
r#"JSON.stringify({aaa: 'bbb'}) == '{"aaa":"bbb"}'"#
),
"true"
);
}
}

18
boa/src/builtins/json/tests.rs

@ -0,0 +1,18 @@
use crate::{exec::Executor, forward, realm::Realm};
#[test]
fn json_sanity() {
let realm = Realm::create();
let mut engine = Executor::new(realm);
assert_eq!(
forward(&mut engine, r#"JSON.parse('{"aaa":"bbb"}').aaa == 'bbb'"#),
"true"
);
assert_eq!(
forward(
&mut engine,
r#"JSON.stringify({aaa: 'bbb'}) == '{"aaa":"bbb"}'"#
),
"true"
);
}

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

@ -1,3 +1,16 @@
//! This module implements the global `Math` object.
//!
//! `Math` is a built-in object that has properties and methods for mathematical constants and functions. It’s not a function object.
//!
//! `Math` works with the `Number` type. It doesn't work with `BigInt`.
//!
//! More information:
//! - [ECMAScript reference][spec]
//! - [MDN documentation][mdn]
//!
//! [spec]: https://tc39.es/ecma262/#sec-math-object
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math
use crate::{ use crate::{
builtins::{ builtins::{
function::NativeFunctionData, function::NativeFunctionData,
@ -11,7 +24,14 @@ use std::f64;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
/// Get the absolute value of a number /// Get the absolute value of a number.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() { Ok(to_value(if args.is_empty() {
f64::NAN f64::NAN
@ -21,7 +41,15 @@ pub fn abs(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.abs() .abs()
})) }))
} }
/// Get the arccos of a number
/// Get the arccos of a number.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() { Ok(to_value(if args.is_empty() {
f64::NAN f64::NAN
@ -31,7 +59,15 @@ pub fn acos(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.acos() .acos()
})) }))
} }
/// Get the hyperbolic arccos of a number
/// Get the hyperbolic arccos of a number.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() { Ok(to_value(if args.is_empty() {
f64::NAN f64::NAN
@ -41,7 +77,15 @@ pub fn acosh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.acosh() .acosh()
})) }))
} }
/// Get the arcsine of a number
/// Get the arcsine of a number.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() { Ok(to_value(if args.is_empty() {
f64::NAN f64::NAN
@ -51,7 +95,15 @@ pub fn asin(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.asin() .asin()
})) }))
} }
/// Get the hyperbolic arcsine of a number
/// Get the hyperbolic arcsine of a number.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() { Ok(to_value(if args.is_empty() {
f64::NAN f64::NAN
@ -61,7 +113,15 @@ pub fn asinh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.asinh() .asinh()
})) }))
} }
/// Get the arctangent of a number
/// Get the arctangent of a number.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() { Ok(to_value(if args.is_empty() {
f64::NAN f64::NAN
@ -71,7 +131,15 @@ pub fn atan(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.atan() .atan()
})) }))
} }
/// Get the hyperbolic arctangent of a number
/// Get the hyperbolic arctangent of a number.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() { Ok(to_value(if args.is_empty() {
f64::NAN f64::NAN
@ -81,7 +149,15 @@ pub fn atanh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.atanh() .atanh()
})) }))
} }
/// Get the arctangent of a numbers
/// Get the arctangent of a numbers.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() { Ok(to_value(if args.is_empty() {
f64::NAN f64::NAN
@ -91,7 +167,15 @@ pub fn atan2(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.atan2(args.get(1).expect("Could not get argument").to_num()) .atan2(args.get(1).expect("Could not get argument").to_num())
})) }))
} }
/// Get the cubic root of a number
/// Get the cubic root of a number.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() { Ok(to_value(if args.is_empty() {
f64::NAN f64::NAN
@ -101,7 +185,15 @@ pub fn cbrt(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.cbrt() .cbrt()
})) }))
} }
/// Get lowest integer above a number
/// Get lowest integer above a number.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() { Ok(to_value(if args.is_empty() {
f64::NAN f64::NAN
@ -111,7 +203,15 @@ pub fn ceil(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.ceil() .ceil()
})) }))
} }
/// Get the cosine of a number
/// Get the cosine of a number.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() { Ok(to_value(if args.is_empty() {
f64::NAN f64::NAN
@ -121,7 +221,15 @@ pub fn cos(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.cos() .cos()
})) }))
} }
/// Get the hyperbolic cosine of a number
/// Get the hyperbolic cosine of a number.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() { Ok(to_value(if args.is_empty() {
f64::NAN f64::NAN
@ -131,7 +239,15 @@ pub fn cosh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.cosh() .cosh()
})) }))
} }
/// Get the power to raise the natural logarithm to get the number
/// Get the power to raise the natural logarithm to get the number.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() { Ok(to_value(if args.is_empty() {
f64::NAN f64::NAN
@ -141,7 +257,15 @@ pub fn exp(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.exp() .exp()
})) }))
} }
/// Get the highest integer below a number
/// Get the highest integer below a number.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() { Ok(to_value(if args.is_empty() {
f64::NAN f64::NAN
@ -151,7 +275,15 @@ pub fn floor(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.floor() .floor()
})) }))
} }
/// Get the natural logarithm of a number
/// Get the natural logarithm of a number.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() { Ok(to_value(if args.is_empty() {
f64::NAN f64::NAN
@ -166,7 +298,15 @@ pub fn log(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
} }
})) }))
} }
/// Get the base 10 logarithm of the number
/// Get the base 10 logarithm of the number.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() { Ok(to_value(if args.is_empty() {
f64::NAN f64::NAN
@ -181,7 +321,15 @@ pub fn log10(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
} }
})) }))
} }
/// Get the base 2 logarithm of the number
/// Get the base 2 logarithm of the number.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() { Ok(to_value(if args.is_empty() {
f64::NAN f64::NAN
@ -196,7 +344,15 @@ pub fn log2(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
} }
})) }))
} }
/// Get the maximum of several numbers
/// Get the maximum of several numbers.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let mut max = f64::NEG_INFINITY; let mut max = f64::NEG_INFINITY;
for arg in args { for arg in args {
@ -205,7 +361,15 @@ pub fn max(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
} }
Ok(to_value(max)) Ok(to_value(max))
} }
/// Get the minimum of several numbers
/// Get the minimum of several numbers.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let mut max = f64::INFINITY; let mut max = f64::INFINITY;
for arg in args { for arg in args {
@ -214,7 +378,15 @@ pub fn min(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
} }
Ok(to_value(max)) Ok(to_value(max))
} }
/// Raise a number to a power
/// Raise a number to a power.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.len() >= 2 { Ok(to_value(if args.len() >= 2 {
let num: f64 = from_value(args.get(0).expect("Could not get argument").clone()) let num: f64 = from_value(args.get(0).expect("Could not get argument").clone())
@ -226,11 +398,27 @@ pub fn pow(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
f64::NAN f64::NAN
})) }))
} }
/// Generate a random floating-point number between 0 and 1
/// Generate a random floating-point number between `0` and `1`.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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(_: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(random::<f64>())) Ok(to_value(random::<f64>()))
} }
/// Round a number to the nearest integer
/// Round a number to the nearest integer.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() { Ok(to_value(if args.is_empty() {
f64::NAN f64::NAN
@ -240,7 +428,15 @@ pub fn round(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.round() .round()
})) }))
} }
/// Get the sign of a number
/// Get the sign of a number.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() { Ok(to_value(if args.is_empty() {
f64::NAN f64::NAN
@ -255,7 +451,15 @@ pub fn sign(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
} }
})) }))
} }
/// Get the sine of a number
/// Get the sine of a number.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() { Ok(to_value(if args.is_empty() {
f64::NAN f64::NAN
@ -265,7 +469,15 @@ pub fn sin(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.sin() .sin()
})) }))
} }
/// Get the hyperbolic sine of a number
/// Get the hyperbolic sine of a number.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() { Ok(to_value(if args.is_empty() {
f64::NAN f64::NAN
@ -275,7 +487,15 @@ pub fn sinh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.sinh() .sinh()
})) }))
} }
/// Get the square root of a number
/// Get the square root of a number.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() { Ok(to_value(if args.is_empty() {
f64::NAN f64::NAN
@ -295,7 +515,15 @@ pub fn tan(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.tan() .tan()
})) }))
} }
/// Get the hyperbolic tangent of a number
/// Get the hyperbolic tangent of a number.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() { Ok(to_value(if args.is_empty() {
f64::NAN f64::NAN
@ -305,7 +533,15 @@ pub fn tanh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.tanh() .tanh()
})) }))
} }
/// Get the integer part of a number
/// Get the integer part of a number.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() { Ok(to_value(if args.is_empty() {
f64::NAN f64::NAN
@ -315,6 +551,7 @@ pub fn trunc(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.trunc() .trunc()
})) }))
} }
/// Create a new `Math` object /// Create a new `Math` object
pub fn create_constructor(global: &Value) -> Value { pub fn create_constructor(global: &Value) -> Value {
let math = ValueData::new_obj(Some(global)); let math = ValueData::new_obj(Some(global));

21
boa/src/builtins/mod.rs

@ -1,4 +1,7 @@
/// Macro to create a new member function of a prototype //! Builtins live here, such as Object, String, Math etc
/// Macro to create a new member function of a prototype.
///
/// If no length is provided, the length will be set to 0. /// If no length is provided, the length will be set to 0.
macro_rules! make_builtin_fn { macro_rules! make_builtin_fn {
($fn:ident, named $name:expr, with length $l:tt, of $p:ident) => { ($fn:ident, named $name:expr, with length $l:tt, of $p:ident) => {
@ -11,31 +14,17 @@ macro_rules! make_builtin_fn {
}; };
} }
/// The global `Array` object
pub mod array; pub mod array;
/// the global `Symbol` Object
pub mod symbol;
// The global `Boolean` object
pub mod boolean; pub mod boolean;
/// The global `console` object
pub mod console; pub mod console;
/// The global `Error` object
pub mod error; pub mod error;
/// The global `Function` object and function value representations
pub mod function; pub mod function;
/// The global `JSON` object
pub mod json; pub mod json;
/// The global `Math` object
pub mod math; pub mod math;
/// The global `Number` object
pub mod number; pub mod number;
/// The global `Object` object
pub mod object; pub mod object;
/// Property, used by `Object`
pub mod property; pub mod property;
/// The global 'RegExp' object
pub mod regexp; pub mod regexp;
/// The global `String` object
pub mod string; pub mod string;
/// Javascript values, utility methods and conversion between Javascript values and Rust values pub mod symbol;
pub mod value; pub mod value;

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

@ -1,3 +1,18 @@
//! This module implements the global `Number` object.
//!
//! The `Number` JavaScript object is a wrapper object allowing you to work with numerical values.
//! A `Number` object is created using the `Number()` constructor. A primitive type object number is created using the `Number()` **function**.
//!
//! The JavaScript `Number` type is double-precision 64-bit binary format IEEE 754 value. In more recent implementations,
//! JavaScript also supports integers with arbitrary precision using the BigInt type.
//!
//! More information:
//! - [ECMAScript reference][spec]
//! - [MDN documentation][mdn]
//!
//! [spec]: https://tc39.es/ecma262/#sec-number-object
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
@ -11,9 +26,7 @@ use crate::{
}; };
use std::{borrow::Borrow, f64, ops::Deref}; use std::{borrow::Borrow, f64, ops::Deref};
/// Helper function: to_number(value: &Value) -> Value /// Helper function that converts a Value to a Number.
///
/// Converts a Value to a Number.
fn to_number(value: &Value) -> Value { fn to_number(value: &Value) -> Value {
match *value.deref().borrow() { match *value.deref().borrow() {
ValueData::Boolean(b) => { ValueData::Boolean(b) => {
@ -35,9 +48,7 @@ fn to_number(value: &Value) -> Value {
} }
} }
/// Helper function: num_to_exponential(n: f64) -> String /// Helper function that formats a float as a ES6-style exponential number string.
///
/// Formats a float as a ES6-style exponential number string.
fn num_to_exponential(n: f64) -> String { fn num_to_exponential(n: f64) -> String {
match n.abs() { match n.abs() {
x if x > 1.0 => format!("{:e}", n).replace("e", "e+"), x if x > 1.0 => format!("{:e}", n).replace("e", "e+"),
@ -46,9 +57,7 @@ fn num_to_exponential(n: f64) -> String {
} }
} }
/// Number(arg) /// Create a new number `[[Construct]]`
///
/// Create a new number [[Construct]]
pub fn make_number(this: &Value, args: &[Value], _ctx: &mut Interpreter) -> ResultValue { pub fn make_number(this: &Value, args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
let data = match args.get(0) { let data = match args.get(0) {
Some(ref value) => to_number(value), Some(ref value) => to_number(value),
@ -58,9 +67,9 @@ pub fn make_number(this: &Value, args: &[Value], _ctx: &mut Interpreter) -> Resu
Ok(this.clone()) Ok(this.clone())
} }
/// Number() /// `Number()` function.
/// ///
/// https://tc39.es/ecma262/#sec-number-constructor-number-value /// 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: &Value, args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
let data = match args.get(0) { let data = match args.get(0) {
Some(ref value) => to_number(value), Some(ref value) => to_number(value),
@ -69,16 +78,32 @@ pub fn call_number(_this: &Value, args: &[Value], _ctx: &mut Interpreter) -> Res
Ok(data) Ok(data)
} }
/// Number().toExponential() /// `Number.prototype.toExponential( [fractionDigits] )`
///
/// The `toExponential()` method returns a string representing the Number object in exponential notation.
/// ///
/// https://tc39.es/ecma262/#sec-number.prototype.toexponential /// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
let this_num = to_number(this).to_num(); let this_num = to_number(this).to_num();
let this_str_num = num_to_exponential(this_num); let this_str_num = num_to_exponential(this_num);
Ok(to_value(this_str_num)) Ok(to_value(this_str_num))
} }
/// https://tc39.es/ecma262/#sec-number.prototype.tofixed /// `Number.prototype.toFixed( [digits] )`
///
/// The `toFixed()` method formats a number using fixed-point notation
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
let this_num = to_number(this).to_num(); let this_num = to_number(this).to_num();
let precision = match args.get(0) { let precision = match args.get(0) {
@ -92,21 +117,35 @@ pub fn to_fixed(this: &Value, args: &[Value], _ctx: &mut Interpreter) -> ResultV
Ok(to_value(this_fixed_num)) Ok(to_value(this_fixed_num))
} }
/// Number().toLocaleString() /// `Number.prototype.toLocaleString( [locales [, options]] )`
/// ///
/// https://tc39.es/ecma262/#sec-number.prototype.tolocalestring /// The `toLocaleString()` method returns a string with a language-sensitive representation of this number.
/// ///
/// Note that while this technically conforms to the Ecma standard, it does no actual /// Note that while this technically conforms to the Ecma standard, it does no actual
/// internationalization logic. /// internationalization logic.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
let this_num = to_number(this).to_num(); let this_num = to_number(this).to_num();
let this_str_num = format!("{}", this_num); let this_str_num = format!("{}", this_num);
Ok(to_value(this_str_num)) Ok(to_value(this_str_num))
} }
/// Number().toPrecision(p) /// `Number.prototype.toPrecision( [precision] )`
///
/// The `toPrecision()` method returns a string representing the Number object to the specified precision.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
/// ///
/// https://tc39.es/ecma262/#sec-number.prototype.toprecision /// [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: &Value, args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
let this_num = to_number(this); let this_num = to_number(this);
let _num_str_len = format!("{}", this_num.to_num()).len(); let _num_str_len = format!("{}", this_num.to_num()).len();
@ -121,16 +160,30 @@ pub fn to_precision(this: &Value, args: &[Value], _ctx: &mut Interpreter) -> Res
unimplemented!("TODO: Implement toPrecision"); unimplemented!("TODO: Implement toPrecision");
} }
/// Number().toString() /// `Number.prototype.toString( [radix] )`
/// ///
/// https://tc39.es/ecma262/#sec-number.prototype.tostring /// The `toString()` method returns a string representing the specified Number object.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
Ok(to_value(format!("{}", to_number(this).to_num()))) Ok(to_value(format!("{}", to_number(this).to_num())))
} }
/// Number().valueOf() /// `Number.prototype.toString()`
///
/// The `valueOf()` method returns the wrapped primitive value of a Number object.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
/// ///
/// https://tc39.es/ecma262/#sec-number.prototype.valueof /// [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: &Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
Ok(to_number(this)) Ok(to_number(this))
} }

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

@ -1,3 +1,10 @@
//! This module defines the `ObjectInternalMethods` trait.
//!
//! More information:
//! - [ECMAScript reference][spec]
//!
//! [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots
use crate::builtins::{ use crate::builtins::{
object::{Object, PROTOTYPE}, object::{Object, PROTOTYPE},
property::Property, property::Property,
@ -7,12 +14,22 @@ use gc::Gc;
use std::borrow::Borrow; use std::borrow::Borrow;
use std::ops::Deref; use std::ops::Deref;
/// Here lies the internal methods for ordinary objects. /// Here lies the internal methods for ordinary objects.
/// Most objects make use of these methods, including exotic objects like functions. ///
/// Most objects make use of these methods, including exotic objects like functions.
/// So thats why this is a trait /// So thats why this is a trait
/// <https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots> ///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots
pub trait ObjectInternalMethods { pub trait ObjectInternalMethods {
/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-hasproperty-p /// Check if has property.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-hasproperty-p
fn has_property(&self, val: &Value) -> bool { fn has_property(&self, val: &Value) -> bool {
debug_assert!(Property::is_property_key(val)); debug_assert!(Property::is_property_key(val));
let prop = self.get_own_property(val); let prop = self.get_own_property(val);
@ -32,7 +49,12 @@ pub trait ObjectInternalMethods {
true true
} }
/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-isextensible /// Check if it is extensible.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-isextensible
fn is_extensible(&self) -> bool { fn is_extensible(&self) -> bool {
let val = self.get_internal_slot("extensible"); let val = self.get_internal_slot("extensible");
match *val.deref().borrow() { match *val.deref().borrow() {
@ -41,13 +63,18 @@ pub trait ObjectInternalMethods {
} }
} }
/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-preventextensions /// Disable extensibility.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-preventextensions
fn prevent_extensions(&mut self) -> bool { fn prevent_extensions(&mut self) -> bool {
self.set_internal_slot("extensible", to_value(false)); self.set_internal_slot("extensible", to_value(false));
true true
} }
// [[Delete]] /// Delete property.
fn delete(&mut self, prop_key: &Value) -> bool { fn delete(&mut self, prop_key: &Value) -> bool {
debug_assert!(Property::is_property_key(prop_key)); debug_assert!(Property::is_property_key(prop_key));
let desc = self.get_own_property(prop_key); let desc = self.get_own_property(prop_key);
@ -102,7 +129,7 @@ pub trait ObjectInternalMethods {
Gc::new(ValueData::Undefined) Gc::new(ValueData::Undefined)
} }
/// [[Set]] /// [[Set]]
/// <https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-set-p-v-receiver> /// <https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-set-p-v-receiver>
fn set(&mut self, field: Value, val: Value) -> bool { fn set(&mut self, field: Value, val: Value) -> bool {
// [1] // [1]

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

@ -1,3 +1,18 @@
//! This module implements the global `Object` object.
//!
//! The `Object` class represents one of JavaScript's data types.
//!
//! It is used to store various keyed collections and more complex entities.
//! Objects can be created using the `Object()` constructor or the
//! object initializer / literal syntax.
//!
//! More information:
//! - [ECMAScript reference][spec]
//! - [MDN documentation][mdn]
//!
//! [spec]: https://tc39.es/ecma262/#sec-objects
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object
use crate::{ use crate::{
builtins::{ builtins::{
function::NativeFunctionData, function::NativeFunctionData,
@ -22,12 +37,12 @@ pub static PROTOTYPE: &str = "prototype";
/// Static `__proto__`, usually set on Object instances as a key to point to their respective prototype object. /// Static `__proto__`, usually set on Object instances as a key to point to their respective prototype object.
pub static INSTANCE_PROTOTYPE: &str = "__proto__"; pub static INSTANCE_PROTOTYPE: &str = "__proto__";
/// `ObjectData` is the representation of an object in JavaScript /// The internal representation of an JavaScript object.
#[derive(Trace, Finalize, Debug, Clone)] #[derive(Trace, Finalize, Debug, Clone)]
pub struct Object { pub struct Object {
/// Kind /// The type of the object.
pub kind: ObjectKind, pub kind: ObjectKind,
/// Internal Slots /// Intfiernal Slots
pub internal_slots: Box<HashMap<String, Value>>, pub internal_slots: Box<HashMap<String, Value>>,
/// Properties /// Properties
pub properties: Box<HashMap<String, Property>>, pub properties: Box<HashMap<String, Property>>,
@ -38,7 +53,17 @@ pub struct Object {
} }
impl ObjectInternalMethods for Object { impl ObjectInternalMethods for Object {
/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-setprototypeof-v /// `Object.setPropertyOf(obj, prototype)`
///
/// This method sets the prototype (i.e., the internal `[[Prototype]]` property)
/// of a specified object to another object or `null`.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-setprototypeof-v
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf
fn set_prototype_of(&mut self, val: Value) -> bool { fn set_prototype_of(&mut self, val: Value) -> bool {
debug_assert!(val.is_object() || val.is_null()); debug_assert!(val.is_object() || val.is_null());
let current = self.get_internal_slot(PROTOTYPE); let current = self.get_internal_slot(PROTOTYPE);
@ -64,20 +89,22 @@ impl ObjectInternalMethods for Object {
true true
} }
/// Helper function for property insertion.
fn insert_property(&mut self, name: String, p: Property) { fn insert_property(&mut self, name: String, p: Property) {
self.properties.insert(name, p); self.properties.insert(name, p);
} }
/// Helper function for property removal.
fn remove_property(&mut self, name: &str) { fn remove_property(&mut self, name: &str) {
self.properties.remove(name); self.properties.remove(name);
} }
/// Utility function to set an internal slot /// Helper function to set an internal slot
fn set_internal_slot(&mut self, name: &str, val: Value) { fn set_internal_slot(&mut self, name: &str, val: Value) {
self.internal_slots.insert(name.to_string(), val); self.internal_slots.insert(name.to_string(), val);
} }
/// Utility function to get an immutable internal slot or Null /// Helper function to get an immutable internal slot or Null
fn get_internal_slot(&self, name: &str) -> Value { fn get_internal_slot(&self, name: &str) -> Value {
match self.internal_slots.get(name) { match self.internal_slots.get(name) {
Some(v) => v.clone(), Some(v) => v.clone(),
@ -85,8 +112,14 @@ impl ObjectInternalMethods for Object {
} }
} }
/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getownproperty-p /// The specification returns a Property Descriptor or Undefined.
/// The specification returns a Property Descriptor or Undefined. These are 2 separate types and we can't do that here. ///
/// These are 2 separate types and we can't do that here.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getownproperty-p
fn get_own_property(&self, prop: &Value) -> Property { fn get_own_property(&self, prop: &Value) -> Property {
debug_assert!(Property::is_property_key(prop)); debug_assert!(Property::is_property_key(prop));
// Prop could either be a String or Symbol // Prop could either be a String or Symbol
@ -143,6 +176,12 @@ impl ObjectInternalMethods for Object {
} }
} }
/// Define an own property.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-defineownproperty-p-desc
#[allow(clippy::option_unwrap_used)] #[allow(clippy::option_unwrap_used)]
fn define_own_property(&mut self, property_key: String, desc: Property) -> bool { 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 mut current = self.get_own_property(&to_value(property_key.to_string()));
@ -285,9 +324,12 @@ impl Object {
object object
} }
/// ObjectCreate is used to specify the runtime creation of new ordinary objects /// ObjectCreate is used to specify the runtime creation of new ordinary objects.
///
/// More information:
/// - [ECMAScript reference][spec]
/// ///
/// https://tc39.es/ecma262/#sec-objectcreate /// [spec]: https://tc39.es/ecma262/#sec-objectcreate
// TODO: proto should be a &Value here // TODO: proto should be a &Value here
pub fn create(proto: Value) -> Object { pub fn create(proto: Value) -> Object {
let mut obj = Object::default(); let mut obj = Object::default();
@ -298,19 +340,20 @@ impl Object {
obj obj
} }
/// Utility function to set an internal slot which is a function /// Utility function to set an internal slot which is a function.
pub fn set_internal_method(&mut self, name: &str, val: NativeFunctionData) { pub fn set_internal_method(&mut self, name: &str, val: NativeFunctionData) {
self.internal_slots.insert(name.to_string(), to_value(val)); self.internal_slots.insert(name.to_string(), to_value(val));
} }
/// Utility function to set a method on this object /// Utility function to set a method on this object.
/// The native function will live in the `properties` field of the Object ///
/// The native function will live in the `properties` field of the Object.
pub fn set_method(&mut self, name: &str, val: NativeFunctionData) { pub fn set_method(&mut self, name: &str, val: NativeFunctionData) {
self.properties self.properties
.insert(name.to_string(), Property::default().value(to_value(val))); .insert(name.to_string(), Property::default().value(to_value(val)));
} }
/// Return a new Boolean object whose [[BooleanData]] internal slot is set to argument. /// Return a new Boolean object whose `[[BooleanData]]` internal slot is set to argument.
fn from_boolean(argument: &Value) -> Self { fn from_boolean(argument: &Value) -> Self {
let mut obj = Object { let mut obj = Object {
kind: ObjectKind::Boolean, kind: ObjectKind::Boolean,
@ -325,7 +368,7 @@ impl Object {
obj obj
} }
/// Return a new Number object whose [[NumberData]] internal slot is set to argument. /// Return a new `Number` object whose `[[NumberData]]` internal slot is set to argument.
fn from_number(argument: &Value) -> Self { fn from_number(argument: &Value) -> Self {
let mut obj = Object { let mut obj = Object {
kind: ObjectKind::Number, kind: ObjectKind::Number,
@ -340,7 +383,7 @@ impl Object {
obj obj
} }
/// Return a new String object whose [[StringData]] internal slot is set to argument. /// Return a new `String` object whose `[[StringData]]` internal slot is set to argument.
fn from_string(argument: &Value) -> Self { fn from_string(argument: &Value) -> Self {
let mut obj = Object { let mut obj = Object {
kind: ObjectKind::String, kind: ObjectKind::String,
@ -355,7 +398,12 @@ impl Object {
obj obj
} }
// https://tc39.es/ecma262/#sec-toobject /// Converts the `Value` to an `Object` type.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-toobject
pub fn from(value: &Value) -> Result<Self, ()> { pub fn from(value: &Value) -> Result<Self, ()> {
match *value.deref().borrow() { match *value.deref().borrow() {
ValueData::Boolean(_) => Ok(Self::from_boolean(value)), ValueData::Boolean(_) => Ok(Self::from_boolean(value)),
@ -367,6 +415,7 @@ impl Object {
} }
} }
/// Defines the different types of objects.
#[derive(Trace, Finalize, Clone, Debug, Eq, PartialEq)] #[derive(Trace, Finalize, Clone, Debug, Eq, PartialEq)]
pub enum ObjectKind { pub enum ObjectKind {
Function, Function,
@ -379,18 +428,18 @@ pub enum ObjectKind {
Number, Number,
} }
/// Create a new object /// Create a new object.
pub fn make_object(_: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { pub fn make_object(_: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(Gc::new(ValueData::Undefined)) Ok(Gc::new(ValueData::Undefined))
} }
/// Get the prototype of an object /// Get the `prototype` of an object.
pub fn get_proto_of(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub fn get_proto_of(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let obj = args.get(0).expect("Cannot get object"); let obj = args.get(0).expect("Cannot get object");
Ok(obj.get_field_slice(INSTANCE_PROTOTYPE)) Ok(obj.get_field_slice(INSTANCE_PROTOTYPE))
} }
/// Set the prototype of an object /// Set the `prototype` of an object.
pub fn set_proto_of(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub fn set_proto_of(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let obj = args.get(0).expect("Cannot get object").clone(); let obj = args.get(0).expect("Cannot get object").clone();
let proto = args.get(1).expect("Cannot get object").clone(); let proto = args.get(1).expect("Cannot get object").clone();
@ -409,12 +458,31 @@ pub fn define_prop(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValu
Ok(Gc::new(ValueData::Undefined)) Ok(Gc::new(ValueData::Undefined))
} }
/// To string /// `Object.prototype.toString()`
///
/// This method returns a string representing the object.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(this.to_string())) Ok(to_value(this.to_string()))
} }
/// Check if it has a property /// `Object.prototype.hasOwnPrototype( property )`
///
/// The method returns a boolean indicating whether the object has the specified property
/// as its own property (as opposed to inheriting it).
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let prop = if args.is_empty() { let prop = if args.is_empty() {
None None
@ -426,7 +494,7 @@ pub fn has_own_prop(this: &Value, args: &[Value], _: &mut Interpreter) -> Result
)) ))
} }
/// Create a new `Object` object /// Create a new `Object` object.
pub fn create_constructor(_: &Value) -> Value { pub fn create_constructor(_: &Value) -> Value {
let object = to_value(make_object as NativeFunctionData); let object = to_value(make_object as NativeFunctionData);
// Prototype chain ends here VV // Prototype chain ends here VV

64
boa/src/builtins/property.rs

@ -1,11 +1,40 @@
//! This module implements the Property Descriptor.
//!
//! The Property Descriptor type is used to explain the manipulation and reification of Object property attributes.
//! Values of the Property Descriptor type are Records. Each field's name is an attribute name
//! and its value is a corresponding attribute value as specified in [6.1.7.1][section].
//! In addition, any field may be present or absent.
//! The schema name used within this specification to tag literal descriptions of Property Descriptor records is “PropertyDescriptor”.
//!
//! More information:
//! - [MDN documentation][mdn]
//! - [ECMAScript reference][spec]
//!
//! [spec]: https://tc39.es/ecma262/#sec-property-descriptor-specification-type
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
//! [section]: https://tc39.es/ecma262/#sec-property-attributes
use crate::builtins::value::{from_value, to_value, FromValue, ToValue, Value, ValueData}; use crate::builtins::value::{from_value, to_value, FromValue, ToValue, Value, ValueData};
use gc_derive::{Finalize, Trace}; use gc_derive::{Finalize, Trace};
/// A Javascript Property AKA The Property Descriptor /// This represents a Javascript Property AKA The Property Descriptor.
/// [[SPEC] - The Property Descriptor Specification Type](https://tc39.es/ecma262/#sec-property-descriptor-specification-type) ///
/// [[SPEC] - Default Attribute Values](https://tc39.es/ecma262/#table-4) /// Property descriptors present in objects come in two main flavors:
/// - data descriptors
/// - accessor descriptors
///
/// A data descriptor is a property that has a value, which may or may not be writable.
/// An accessor descriptor is a property described by a getter-setter pair of functions.
/// A descriptor must be one of these two flavors; it cannot be both.
/// ///
/// Any field in a JavaScript Property may be present or absent. /// Any field in a JavaScript Property may be present or absent.
///
/// More information:
/// - [MDN documentation][mdn]
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-property-descriptor-specification-type
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
#[derive(Trace, Finalize, Clone, Debug)] #[derive(Trace, Finalize, Clone, Debug)]
pub struct Property { pub struct Property {
/// If the type of this can be changed and this can be deleted /// If the type of this can be changed and this can be deleted
@ -91,19 +120,32 @@ impl Property {
&& self.enumerable.is_none() && self.enumerable.is_none()
} }
/// An accessor Property Descriptor is one that includes any fields named either [[Get]] or [[Set]]. /// An accessor Property Descriptor is one that includes any fields named either [[Get]] or [[Set]].
/// <https://tc39.es/ecma262/#sec-isaccessordescriptor> ///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-isaccessordescriptor
pub fn is_accessor_descriptor(&self) -> bool { pub fn is_accessor_descriptor(&self) -> bool {
self.get.is_some() || self.set.is_some() self.get.is_some() || self.set.is_some()
} }
/// A data Property Descriptor is one that includes any fields named either [[Value]] or [[Writable]]. /// A data Property Descriptor is one that includes any fields named either [[Value]] or [[Writable]].
/// https://tc39.es/ecma262/#sec-isdatadescriptor ///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-isdatadescriptor
pub fn is_data_descriptor(&self) -> bool { pub fn is_data_descriptor(&self) -> bool {
self.value.is_some() || self.writable.is_some() self.value.is_some() || self.writable.is_some()
} }
/// https://tc39.es/ecma262/#sec-isgenericdescriptor /// Check if a property is generic.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-isgenericdescriptor
pub fn is_generic_descriptor(&self) -> bool { pub fn is_generic_descriptor(&self) -> bool {
!self.is_accessor_descriptor() && !self.is_data_descriptor() !self.is_accessor_descriptor() && !self.is_data_descriptor()
} }
@ -111,7 +153,11 @@ impl Property {
impl Default for Property { impl Default for Property {
/// Make a default property /// Make a default property
/// https://tc39.es/ecma262/#table-default-attribute-values ///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#table-default-attribute-values
fn default() -> Self { fn default() -> Self {
Self { Self {
configurable: None, configurable: None,

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

@ -1,3 +1,14 @@
//! This module implements the global `RegExp` object.
//!
//! `The `RegExp` object is used for matching text with a pattern.
//!
//! More information:
//! - [ECMAScript reference][spec]
//! - [MDN documentation][mdn]
//!
//! [spec]: https://tc39.es/ecma262/#sec-regexp-constructor
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp
use std::ops::Deref; use std::ops::Deref;
use gc::Gc; use gc::Gc;
@ -13,30 +24,40 @@ use crate::{
exec::Interpreter, exec::Interpreter,
}; };
/// The internal representation on a `RegExp` object.
#[derive(Debug)] #[derive(Debug)]
struct RegExp { struct RegExp {
/// Regex matcher. /// Regex matcher.
matcher: Regex, matcher: Regex,
/// Update last_index, set if global or sticky flags are set. /// Update last_index, set if global or sticky flags are set.
use_last_index: bool, use_last_index: bool,
/// String of parsed flags. /// String of parsed flags.
flags: String, flags: String,
/// Flag 's' - dot matches newline characters. /// Flag 's' - dot matches newline characters.
dot_all: bool, dot_all: bool,
/// Flag 'g' /// Flag 'g'
global: bool, global: bool,
/// Flag 'i' - ignore case. /// Flag 'i' - ignore case.
ignore_case: bool, ignore_case: bool,
/// Flag 'm' - '^' and '$' match beginning/end of line. /// Flag 'm' - '^' and '$' match beginning/end of line.
multiline: bool, multiline: bool,
/// Flag 'y' /// Flag 'y'
sticky: bool, sticky: bool,
/// Flag 'u' - Unicode. /// Flag 'u' - Unicode.
unicode: bool, unicode: bool,
} }
impl InternalState for RegExp {} impl InternalState for RegExp {}
/// Helper function for getting an argument.
fn get_argument<T: FromValue>(args: &[Value], idx: usize) -> Result<T, Value> { fn get_argument<T: FromValue>(args: &[Value], idx: usize) -> Result<T, Value> {
match args.get(idx) { match args.get(idx) {
Some(arg) => from_value(arg.clone()).map_err(to_value), Some(arg) => from_value(arg.clone()).map_err(to_value),
@ -150,43 +171,138 @@ pub fn make_regexp(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultV
Ok(this.clone()) Ok(this.clone())
} }
/// `RegExp.prototype.dotAll`
///
/// The `dotAll` property indicates whether or not the "`s`" flag is used with the regular expression.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.dot_all))) this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.dot_all)))
} }
/// `RegExp.prototype.flags`
///
/// The `flags` property returns a string consisting of the [`flags`][flags] of the current regular expression object.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.flags.clone()))) this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.flags.clone())))
} }
/// `RegExp.prototype.global`
///
/// The `global` property indicates whether or not the "`g`" flag is used with the regular expression.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.global))) this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.global)))
} }
/// `RegExp.prototype.ignoreCase`
///
/// The `ignoreCase` property indicates whether or not the "`i`" flag is used with the regular expression.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.ignore_case))) this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.ignore_case)))
} }
/// `RegExp.prototype.multiline`
///
/// The multiline property indicates whether or not the "m" flag is used with the regular expression.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.multiline))) this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.multiline)))
} }
/// `RegExp.prototype.source`
///
/// The `source` property returns a `String` containing the source text of the regexp object,
/// and it doesn't contain the two forward slashes on both sides and any flags.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(this.get_internal_slot("OriginalSource")) Ok(this.get_internal_slot("OriginalSource"))
} }
/// `RegExp.prototype.sticky`
///
/// The `flags` property returns a string consisting of the [`flags`][flags] of the current regular expression object.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.sticky))) this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.sticky)))
} }
/// `RegExp.prototype.unicode`
///
/// The unicode property indicates whether or not the "`u`" flag is used with a regular expression.
/// unicode is a read-only property of an individual regular expression instance.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.unicode))) this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.unicode)))
} }
/// Helper function.
fn _make_prop(getter: NativeFunctionData) -> Property { fn _make_prop(getter: NativeFunctionData) -> Property {
Property::default().get(to_value(getter)) Property::default().get(to_value(getter))
} }
/// Search for a match between this regex and a specified string /// `RegExp.prototype.test( string )`
///
/// The `test()` method executes a search for a match between a regular expression and a specified string.
///
/// Returns `true` or `false`.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let arg_str = get_argument::<String>(args, 0)?; let arg_str = get_argument::<String>(args, 0)?;
let mut last_index = let mut last_index =
@ -209,7 +325,18 @@ pub fn test(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
result result
} }
/// Search for a match between this regex and a specified string /// `RegExp.prototype.exec( string )`
///
/// The exec() method executes a search for a match in a specified string.
///
/// Returns a result array, or `null`.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let arg_str = get_argument::<String>(args, 0)?; let arg_str = get_argument::<String>(args, 0)?;
let mut last_index = let mut last_index =
@ -250,8 +377,16 @@ pub fn exec(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
result result
} }
/// RegExp.prototype[Symbol.match] /// `RegExp.prototype[ @@match ]( string )`
/// Returns matches of the regular expression against a string ///
/// This method retrieves the matches when matching a string against a regular expression.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, arg: String, ctx: &mut Interpreter) -> ResultValue {
let (matcher, flags) = let (matcher, flags) =
this.with_internal_state_ref(|regex: &RegExp| (regex.matcher.clone(), regex.flags.clone())); this.with_internal_state_ref(|regex: &RegExp| (regex.matcher.clone(), regex.flags.clone()));
@ -269,16 +404,33 @@ pub fn r#match(this: &Value, arg: String, ctx: &mut Interpreter) -> ResultValue
} }
} }
/// Return a string representing the regular expression /// `RegExp.prototype.toString()`
///
/// Return a string representing the regular expression.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let body = from_value::<String>(this.get_internal_slot("OriginalSource")).map_err(to_value)?; 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()); let flags = this.with_internal_state_ref(|regex: &RegExp| regex.flags.clone());
Ok(to_value(format!("/{}/{}", body, flags))) Ok(to_value(format!("/{}/{}", body, flags)))
} }
/// RegExp.prototype[Symbol.matchAll] /// `RegExp.prototype[ @@matchAll ]( string )`
/// Returns all matches of the regular expression against a string ///
/// TODO: it's returning an array, it should return an iterator /// The `[@@matchAll]` method returns all matches of the regular expression against a string.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, arg_str: String) -> ResultValue {
let matches: Vec<Value> = this.with_internal_state_ref(|regex: &RegExp| { let matches: Vec<Value> = this.with_internal_state_ref(|regex: &RegExp| {
let mut matches = Vec::new(); let mut matches = Vec::new();
@ -319,7 +471,7 @@ pub fn match_all(this: &Value, arg_str: String) -> ResultValue {
Ok(result) Ok(result)
} }
/// Create a new `RegExp` object /// Create a new `RegExp` object.
pub fn create_constructor(global: &Value) -> Value { pub fn create_constructor(global: &Value) -> Value {
// Create constructor function // Create constructor function
let mut regexp_constructor = Object::default(); let mut regexp_constructor = Object::default();

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

@ -1,3 +1,14 @@
//! This module implements the global `String` object.
//!
//! The `String` global object is a constructor for strings or a sequence of characters.
//!
//! More information:
//! - [ECMAScript reference][spec]
//! - [MDN documentation][mdn]
//!
//! [spec]: https://tc39.es/ecma262/#sec-string-object
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
@ -20,7 +31,6 @@ use std::{
}; };
/// Create new string [[Construct]] /// Create new string [[Construct]]
/// <https://searchfox.org/mozilla-central/source/js/src/vm/StringObject.h#19>
// This gets called when a new String() is created, it's called by exec:346 // 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: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
// If we're constructing a string, we should set the initial length // If we're constructing a string, we should set the initial length
@ -41,7 +51,8 @@ pub fn make_string(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultV
} }
/// Call new string [[Call]] /// Call new string [[Call]]
/// https://tc39.es/ecma262/#sec-string-constructor-string-value ///
/// 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(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let arg = match args.get(0) { let arg = match args.get(0) {
Some(v) => v.clone(), Some(v) => v.clone(),
@ -62,10 +73,22 @@ pub fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue
Ok(to_value(format!("{}", primitive_val))) Ok(to_value(format!("{}", primitive_val)))
} }
/// Returns a single element String containing the code unit at index pos within the String value /// `String.prototype.charAt( index )`
/// resulting from converting this object to a String. If there is no element at that index, the ///
/// result is the empty String. The result is a String value, not a String object. /// The `String` object's `charAt()` method returns a new string consisting of the single UTF-16 code unit located at the specified offset into the string.
/// <https://tc39.es/ecma262/#sec-string.prototype.charat> ///
/// Characters in a string are indexed from left to right. The index of the first character is `0`,
/// and the index of the last character—in a string called `stringName`—is `stringName.length - 1`.
/// If the `index` you supply is out of this range, JavaScript returns an empty string.
///
/// If no index is provided to `charAt()`, the default is `0`.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &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. // 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 // Then we convert it into a Rust String by wrapping it in from_value
@ -96,10 +119,20 @@ pub fn char_at(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultVal
)) ))
} }
/// Returns a Number (a nonnegative integer less than 216) that is the numeric value of the code /// `String.prototype.charCodeAt( index )`
/// unit at index pos within the String resulting from converting this object to a String. If there ///
/// is no element at that index, the result is NaN. /// The `charCodeAt()` method returns an integer between `0` and `65535` representing the UTF-16 code unit at the given index.
/// <https://tc39.es/ecma262/#sec-string.prototype.charcodeat> ///
/// Unicode code points range from `0` to `1114111` (`0x10FFFF`). The first 128 Unicode code points are a direct match of the ASCII character encoding.
///
/// `charCodeAt()` returns `NaN` if the given index is less than `0`, or if it is equal to or greater than the `length` of the string.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &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. // 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 // Then we convert it into a Rust String by wrapping it in from_value
@ -128,9 +161,20 @@ pub fn char_code_at(this: &Value, args: &[Value], ctx: &mut Interpreter) -> Resu
Ok(to_value(f64::from(utf16_val))) Ok(to_value(f64::from(utf16_val)))
} }
/// Returns a String that is the result of concatenating this String and all strings provided as /// `String.prototype.concat( str1[, ...strN] )`
/// arguments ///
/// <https://tc39.es/ecma262/#sec-string.prototype.concat> /// The `concat()` method concatenates the string arguments to the calling string and returns a new string.
///
/// Changes to the original string or the returned string don't affect the other.
///
/// If the arguments are not of the type string, they are converted to string values before concatenating.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &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. // 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 // Then we convert it into a Rust String by wrapping it in from_value
@ -144,9 +188,17 @@ pub fn concat(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValu
Ok(to_value(new_str)) Ok(to_value(new_str))
} }
/// Returns a String that is the result of repeating this String the number of times given by the /// `String.prototype.repeat( count )`
/// first argument ///
/// <https://tc39.es/ecma262/#sec-string.prototype.repeat> /// The `repeat()` method constructs and returns a new string which contains the specified number of
/// copies of the string on which it was called, concatenated together.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &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. // 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 // Then we convert it into a Rust String by wrapping it in from_value
@ -161,9 +213,16 @@ pub fn repeat(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValu
Ok(to_value(primitive_val.repeat(repeat_times))) Ok(to_value(primitive_val.repeat(repeat_times)))
} }
/// Returns a String which contains the slice of the JS String from character at "start" index up /// `String.prototype.slice( beginIndex [, endIndex] )`
/// to but not including character at "end" index ///
/// <https://tc39.es/ecma262/#sec-string.prototype.slice> /// The `slice()` method extracts a section of a string and returns it as a new string, without modifying the original string.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &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. // 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 // Then we convert it into a Rust String by wrapping it in from_value
@ -211,10 +270,16 @@ pub fn slice(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue
Ok(to_value(new_str)) Ok(to_value(new_str))
} }
/// Returns a Boolean indicating whether the sequence of code units of the /// `String.prototype.startWith( searchString[, position] )`
/// "search string" is the same as the corresponding code units of this string ///
/// starting at index "position" /// The `startsWith()` method determines whether a string begins with the characters of a specified string, returning `true` or `false` as appropriate.
/// <https://tc39.es/ecma262/#sec-string.prototype.startswith> ///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &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. // 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 // Then we convert it into a Rust String by wrapping it in from_value
@ -250,10 +315,16 @@ pub fn starts_with(this: &Value, args: &[Value], ctx: &mut Interpreter) -> Resul
} }
} }
/// Returns a Boolean indicating whether the sequence of code units of the /// `String.prototype.endsWith( searchString[, length] )`
/// "search string" is the same as the corresponding code units of this string ///
/// starting at position "end position" - length /// The `endsWith()` method determines whether a string ends with the characters of a specified string, returning `true` or `false` as appropriate.
/// <https://tc39.es/ecma262/#sec-string.prototype.endswith> ///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &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. // 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 // Then we convert it into a Rust String by wrapping it in from_value
@ -291,11 +362,16 @@ pub fn ends_with(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultV
} }
} }
/// Returns a Boolean indicating whether searchString appears as a substring of /// `String.prototype.includes( searchString[, position] )`
/// the result of converting this object to a String, at one or more indices ///
/// that are greater than or equal to position. If position is undefined, 0 is /// The `includes()` method determines whether one string may be found within another string, returning `true` or `false` as appropriate.
/// assumed, so as to search all of the String. ///
/// <https://tc39.es/ecma262/#sec-string.prototype.includes> /// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &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. // 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 // Then we convert it into a Rust String by wrapping it in from_value
@ -346,7 +422,21 @@ fn get_regex_string(value: &Value) -> String {
} }
} }
/// <https://tc39.es/ecma262/#sec-string.prototype.replace> /// `String.prototype.replace( regexp|substr, newSubstr|function )`
///
/// The `replace()` method returns a new string with some or all matches of a `pattern` replaced by a `replacement`.
///
/// The `pattern` can be a string or a `RegExp`, and the `replacement` can be a string or a function to be called for each match.
/// If `pattern` is a string, only the first occurrence will be replaced.
///
/// The original string is left unchanged.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
// TODO: Support Symbol replacer // TODO: Support Symbol replacer
let primitive_val: String = ctx.value_to_rust_string(this); let primitive_val: String = ctx.value_to_rust_string(this);
@ -437,12 +527,18 @@ pub fn replace(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultVal
))) )))
} }
/// If searchString appears as a substring of the result of converting this /// `String.prototype.indexOf( searchValue[, fromIndex] )`
/// object to a String, at one or more indices that are greater than or equal to ///
/// position, then the smallest such index is returned; otherwise, -1 is /// The `indexOf()` method returns the index within the calling `String` object of the first occurrence of the specified value, starting the search at `fromIndex`.
/// returned. If position is undefined, 0 is assumed, so as to search all of the ///
/// String. /// Returns -1 if the value is not found.
/// <https://tc39.es/ecma262/#sec-string.prototype.includes> ///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &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. // 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 // Then we convert it into a Rust String by wrapping it in from_value
@ -483,12 +579,18 @@ pub fn index_of(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultVa
Ok(to_value(-1)) Ok(to_value(-1))
} }
//// If searchString appears as a substring of the result of converting this /// `String.prototype.lastIndexOf( searchValue[, fromIndex] )`
/// object to a String at one or more indices that are smaller than or equal to ///
/// position, then the greatest such index is returned; otherwise, -1 is /// The `lastIndexOf()` method returns the index within the calling `String` object of the last occurrence of the specified value, searching backwards from `fromIndex`.
/// returned. If position is undefined, the length of the String value is ///
/// assumed, so as to search all of the String. /// Returns -1 if the value is not found.
/// <https://tc39.es/ecma262/#sec-string.prototype.lastindexof> ///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &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. // 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 // Then we convert it into a Rust String by wrapping it in from_value
@ -530,16 +632,24 @@ pub fn last_index_of(this: &Value, args: &[Value], ctx: &mut Interpreter) -> Res
Ok(to_value(highest_index)) Ok(to_value(highest_index))
} }
/// Returns an array whose contents is all the results matching the regular expression, if the global (g) flag is present, /// `String.prototype.match( regexp )`
/// in its absence, only the first complete match and its related capturing groups is returned, ///
/// otherwise null is returned if no match is found. /// The `match()` method retrieves the result of matching a **string** against a [`regular expression`][regex].
/// <https://tc39.es/ecma262/#sec-string.prototype.match> ///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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 { pub fn r#match(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let re = make_regexp(&to_value(Object::default()), &[args[0].clone()], ctx)?; let re = make_regexp(&to_value(Object::default()), &[args[0].clone()], ctx)?;
regexp_match(&re, ctx.value_to_rust_string(this), ctx) regexp_match(&re, ctx.value_to_rust_string(this), ctx)
} }
/// Abstract method `StringPad` /// Abstract method `StringPad`.
///
/// Performs the actual string padding for padStart/End. /// Performs the actual string padding for padStart/End.
/// <https://tc39.es/ecma262/#sec-stringpad/> /// <https://tc39.es/ecma262/#sec-stringpad/>
fn string_pad( fn string_pad(
@ -579,11 +689,18 @@ fn string_pad(
} }
} }
/// String.prototype.padEnd ( maxLength [ , fillString ] ) /// `String.prototype.padEnd( targetLength[, padString] )`
///
/// The `padEnd()` method pads the current string with a given string (repeated, if needed) so that the resulting string reaches a given length.
/// ///
/// Pads the string with the given filler at the end of the string. /// The padding is applied from the end of the current string.
/// Filler defaults to single space. ///
/// <https://tc39.es/ecma262/#sec-string.prototype.padend/> /// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let primitive_val: String = ctx.value_to_rust_string(this); let primitive_val: String = ctx.value_to_rust_string(this);
if args.is_empty() { if args.is_empty() {
@ -606,11 +723,18 @@ pub fn pad_end(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultVal
string_pad(primitive_val, max_length, fill_string, false) string_pad(primitive_val, max_length, fill_string, false)
} }
/// String.prototype.padStart ( maxLength [ , fillString ] ) /// `String.prototype.padStart( targetLength [, padString] )`
///
/// The `padStart()` method pads the current string with another string (multiple times, if needed) until the resulting string reaches the given length.
///
/// The padding is applied from the start of the current string.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
/// ///
/// Pads the string with the given filler at the start of the string. /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.padstart
/// Filler defaults to single space. /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart
/// <https://tc39.es/ecma262/#sec-string.prototype.padstart/>
pub fn pad_start(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub fn pad_start(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let primitive_val: String = ctx.value_to_rust_string(this); let primitive_val: String = ctx.value_to_rust_string(this);
if args.is_empty() { if args.is_empty() {
@ -633,6 +757,7 @@ pub fn pad_start(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultV
string_pad(primitive_val, max_length, fill_string, true) string_pad(primitive_val, max_length, fill_string, true)
} }
/// Helper function to check if a `char` is trimmable.
fn is_trimmable_whitespace(c: char) -> bool { fn is_trimmable_whitespace(c: char) -> bool {
// The rust implementation of `trim` does not regard the same characters whitespace as ecma standard does // The rust implementation of `trim` does not regard the same characters whitespace as ecma standard does
// //
@ -651,11 +776,35 @@ fn is_trimmable_whitespace(c: char) -> bool {
} }
} }
/// String.prototype.trim()
///
/// The `trim()` method removes whitespace from both ends of a string.
///
/// Whitespace in this context is all the whitespace characters (space, tab, no-break space, etc.) and all the line terminator characters (LF, CR, etc.).
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
let this_str: String = ctx.value_to_rust_string(this); let this_str: String = ctx.value_to_rust_string(this);
Ok(to_value(this_str.trim_matches(is_trimmable_whitespace))) Ok(to_value(this_str.trim_matches(is_trimmable_whitespace)))
} }
/// `String.prototype.trimStart()`
///
/// The `trimStart()` method removes whitespace from the beginning of a string.
///
/// Whitespace in this context is all the whitespace characters (space, tab, no-break space, etc.) and all the line terminator characters (LF, CR, etc.).
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
let this_str: String = ctx.value_to_rust_string(this); let this_str: String = ctx.value_to_rust_string(this);
Ok(to_value( Ok(to_value(
@ -663,14 +812,33 @@ pub fn trim_start(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultVal
)) ))
} }
/// String.prototype.trimEnd()
///
/// The `trimEnd()` method removes whitespace from the end of a string.
///
/// Whitespace in this context is all the whitespace characters (space, tab, no-break space, etc.) and all the line terminator characters (LF, CR, etc.).
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
let this_str: String = ctx.value_to_rust_string(this); let this_str: String = ctx.value_to_rust_string(this);
Ok(to_value(this_str.trim_end_matches(is_trimmable_whitespace))) Ok(to_value(this_str.trim_end_matches(is_trimmable_whitespace)))
} }
/// Return a String with every code point mapped to its corresponding lowercase equivalent. /// `String.prototype.toLowerCase()`
/// With the current implementation the string is always copied even if the resulting String is identical ///
/// <https://tc39.es/ecma262/#sec-string.prototype.tolowercase> /// The `toLowerCase()` method returns the calling string value converted to lower case.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &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. // 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 // Then we convert it into a Rust String by wrapping it in from_value
@ -680,9 +848,18 @@ pub fn to_lowercase(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultV
Ok(to_value(this_str.to_lowercase())) Ok(to_value(this_str.to_lowercase()))
} }
/// Return a String with every code point mapped to its corresponding uppercase equivalent. /// `String.prototype.toUpperCase()`
/// With the current implementation the string is always copied even if the resulting String is identical ///
/// <https://tc39.es/ecma262/#sec-string.prototype.touppercase> /// The `toUpperCase()` method returns the calling string value converted to uppercase.
///
/// The value will be **converted** to a string if it isn't one
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &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. // 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 // Then we convert it into a Rust String by wrapping it in from_value
@ -692,13 +869,16 @@ pub fn to_uppercase(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultV
Ok(to_value(this_str.to_uppercase())) Ok(to_value(this_str.to_uppercase()))
} }
/// Return a String which is a subset of the String value resulting from converting this object to a String. /// `String.prototype.substring( indexStart[, indexEnd] )`
/// The subset of the string is contained between the start index and the end index. ///
/// When both the start and end arguments are specified, the smaller one represent the index of the code unit /// The `substring()` method returns the part of the `string` between the start and end indexes, or to the end of the string.
/// from which the returned String will start and the larger one the index of the code unit just after the end. ///
/// When only the start index is specified, the end index defaults to being the length of the string. /// More information:
/// When no argument is specified, the returned String is the same as the original /// - [ECMAScript reference][spec]
/// <https://tc39.es/ecma262/#sec-string.prototype.substring> /// - [MDN documentation][mdn]
///
/// [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: &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. // 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 // Then we convert it into a Rust String by wrapping it in from_value
@ -739,11 +919,16 @@ pub fn substring(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultV
Ok(to_value(extracted_string)) Ok(to_value(extracted_string))
} }
/// Return a String which is a subset of the String value resulting from converting this object to a String. /// `String.prototype.substr( start[, length] )`
/// The subset of the string starts at the start index and is at most length code units long, depending if the string is shorter. ///
/// When only the start index is specified, the length become the length of the string. /// The `substr()` method returns a portion of the string, starting at the specified index and extending for a given number of characters afterward.
/// When the start index is negative, the start index become the number of code units from the end of the string. ///
/// When no argument is specified, the returned String is the same as the original /// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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> /// <https://tc39.es/ecma262/#sec-string.prototype.substr>
pub fn substr(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub fn substr(this: &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. // First we get it the actual string a private field stored on the object only the engine has access to.
@ -792,16 +977,34 @@ pub fn substr(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValu
} }
} }
/// Get the string value to a primitive string /// String.prototype.valueOf()
/// <https://tc39.es/ecma262/#sec-string.prototype.valueof> ///
/// The `valueOf()` method returns the primitive value of a `String` object.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
// Use the to_string method because it is specified to do the same thing in this case // Use the to_string method because it is specified to do the same thing in this case
to_string(this, args, ctx) to_string(this, args, ctx)
} }
/// TODO: update this method to return iterator /// `String.prototype.matchAll( regexp )`
/// Returns an array* of all results matching a string against a regular expression, including capturing groups ///
/// <https://tc39.es/ecma262/#sec-string.prototype.matchall> /// The `matchAll()` method returns an iterator of all results matching a string against a [`regular expression`][regex], including [capturing groups][cg].
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.matchall
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/matchAll
/// [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 { pub fn match_all(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let re: Value = match args.get(0) { let re: Value = match args.get(0) {
Some(arg) => { Some(arg) => {

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

@ -1,3 +1,20 @@
//! This module implements the global `Symbol` object.
//!
//! The data type symbol is a primitive data type.
//! The `Symbol()` function returns a value of type symbol, has static properties that expose
//! several members of built-in objects, has static methods that expose the global symbol registry,
//! and resembles a built-in object class, but is incomplete as a constructor because it does not
//! support the syntax "`new Symbol()`".
//!
//! Every symbol value returned from `Symbol()` is unique.
//!
//! More information:
//! - [MDN documentation][mdn]
//! - [ECMAScript reference][spec]
//!
//! [spec]: https://tc39.es/ecma262/#sec-symbol-value
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
@ -14,12 +31,16 @@ use crate::{
use gc::{Gc, GcCell}; use gc::{Gc, GcCell};
use rand::random; use rand::random;
/// https://tc39.es/ecma262/#sec-symbol-description
/// Creates Symbol instances. /// Creates Symbol instances.
/// ///
/// Symbol instances are ordinary objects that inherit properties from the Symbol prototype object. /// Symbol instances are ordinary objects that inherit properties from the Symbol prototype object.
/// Symbol instances have a [[SymbolData]] internal slot. /// Symbol instances have a `[[SymbolData]]` internal slot.
/// The [[SymbolData]] internal slot is the Symbol value represented by this Symbol object. /// The `[[SymbolData]]` internal slot is the Symbol value represented by this Symbol object.
///
/// More information:
/// - [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(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
// From an implementation and specificaition perspective Symbols are similar to Objects. // 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. // They have internal slots to hold the SymbolData and Description, they also have methods and a prototype.
@ -48,14 +69,32 @@ pub fn call_symbol(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultVa
Ok(Gc::new(ValueData::Symbol(GcCell::new(sym_instance)))) Ok(Gc::new(ValueData::Symbol(GcCell::new(sym_instance))))
} }
/// <https://tc39.es/ecma262/#sec-symbol.prototype.tostring> /// `Symbol.prototype.toString()`
///
/// This method returns a string representing the specified `Symbol` object.
///
/// /// More information:
/// - [MDN documentation][mdn]
/// - [ECMAScript reference][spec]
///
/// [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: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let s: Value = this.get_internal_slot("Description"); let s: Value = this.get_internal_slot("Description");
let full_string = format!(r#"Symbol({})"#, s.to_string()); let full_string = format!(r#"Symbol({})"#, s.to_string());
Ok(to_value(full_string)) Ok(to_value(full_string))
} }
/// <https://tc39.es/ecma262/#sec-symbol-constructor> /// The `Symbol()` constructor returns a value of type **symbol**.
///
/// It is incomplete as a constructor because it does not support the syntax "`new Symbol()`".
///
/// More information:
/// - [MDN documentation][mdn]
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-symbol-constructor
/// [mdn]:
pub fn create_constructor(global: &Value) -> Value { pub fn create_constructor(global: &Value) -> Value {
// Create Symbol constructor (or function in Symbol's case) // Create Symbol constructor (or function in Symbol's case)
let mut symbol_constructor = Object::default(); let mut symbol_constructor = Object::default();

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

@ -1,3 +1,7 @@
//! This module implements the JavaScript Value.
//!
//! Javascript values, utility methods and conversion between Javascript values and Rust values.
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
@ -24,7 +28,8 @@ use std::{
/// The result of a Javascript expression is represented like this so it can succeed (`Ok`) or fail (`Err`) /// The result of a Javascript expression is represented like this so it can succeed (`Ok`) or fail (`Err`)
#[must_use] #[must_use]
pub type ResultValue = Result<Value, Value>; pub type ResultValue = Result<Value, Value>;
/// A Garbage-collected Javascript value as represented in the interpreter
/// A Garbage-collected Javascript value as represented in the interpreter.
pub type Value = Gc<ValueData>; pub type Value = Gc<ValueData>;
pub fn undefined() -> Value { pub fn undefined() -> Value {
@ -80,8 +85,13 @@ impl ValueData {
Gc::new(ValueData::Object(GcCell::new(obj))) Gc::new(ValueData::Object(GcCell::new(obj)))
} }
/// This will tell us if we can exten an object or not, not properly implemented yet, for now always returns true /// This will tell us if we can exten an object or not, not properly implemented yet
/// For scalar types it should be false, for objects check the private field for extensibilaty. By default true ///
/// For now always returns true.
///
/// For scalar types it should be false, for objects check the private field for extensibilaty.
/// By default true.
///
/// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal would turn extensible to false/> /// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal would turn extensible to false/>
/// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze would also turn extensible to false/> /// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze would also turn extensible to false/>
pub fn is_extensible(&self) -> bool { pub fn is_extensible(&self) -> bool {
@ -167,6 +177,7 @@ impl ValueData {
} }
/// Returns true if the value is true /// Returns true if the value is true
///
/// [toBoolean](https://tc39.es/ecma262/#sec-toboolean) /// [toBoolean](https://tc39.es/ecma262/#sec-toboolean)
pub fn is_true(&self) -> bool { pub fn is_true(&self) -> bool {
match *self { match *self {
@ -217,6 +228,7 @@ impl ValueData {
} }
/// remove_prop removes a property from a Value object. /// remove_prop removes a property from a Value object.
///
/// It will return a boolean based on if the value was removed, if there was no value to remove false is returned /// It will return a boolean based on if the value was removed, if there was no value to remove false is returned
pub fn remove_prop(&self, field: &str) { pub fn remove_prop(&self, field: &str) {
match *self { match *self {
@ -230,8 +242,9 @@ impl ValueData {
}; };
} }
/// Resolve the property in the object /// Resolve the property in the object.
/// Returns a copy of the Property ///
/// A copy of the Property is returned.
pub fn get_prop(&self, field: &str) -> Option<Property> { pub fn get_prop(&self, field: &str) -> Option<Property> {
// Spidermonkey has its own GetLengthProperty: https://searchfox.org/mozilla-central/source/js/src/vm/Interpreter-inl.h#154 // Spidermonkey has its own GetLengthProperty: https://searchfox.org/mozilla-central/source/js/src/vm/Interpreter-inl.h#154
// This is only for primitive strings, String() objects have their lengths calculated in string.rs // This is only for primitive strings, String() objects have their lengths calculated in string.rs
@ -301,8 +314,9 @@ impl ValueData {
} }
} }
/// Resolve the property in the object /// Resolve the property in the object.
/// Returns a copy of the Property ///
/// Returns a copy of the Property.
pub fn get_internal_slot(&self, field: &str) -> Value { pub fn get_internal_slot(&self, field: &str) -> Value {
let obj: Object = match *self { let obj: Object = match *self {
ValueData::Object(ref obj) => { ValueData::Object(ref obj) => {
@ -578,6 +592,7 @@ impl ValueData {
} }
} }
/// Conversts the `Value` to `JSON`.
pub fn to_json(&self) -> JSONValue { pub fn to_json(&self) -> JSONValue {
match *self { match *self {
ValueData::Null ValueData::Null
@ -603,6 +618,7 @@ impl ValueData {
} }
/// Get the type of the value /// Get the type of the value
///
/// https://tc39.es/ecma262/#sec-typeof-operator /// https://tc39.es/ecma262/#sec-typeof-operator
pub fn get_type(&self) -> &'static str { pub fn get_type(&self) -> &'static str {
match *self { match *self {

2
boa/src/environment/mod.rs

@ -1,3 +1,5 @@
//! Environment handling, lexical, object, function and declaritive records
pub mod declarative_environment_record; pub mod declarative_environment_record;
pub mod environment_record_trait; pub mod environment_record_trait;
pub mod function_environment_record; pub mod function_environment_record;

2
boa/src/exec/mod.rs

@ -1,3 +1,5 @@
//! Execution of the AST, this is where the interpreter actually runs
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;

5
boa/src/lib.rs

@ -12,7 +12,7 @@
unused_lifetimes, unused_lifetimes,
unreachable_pub, unreachable_pub,
trivial_numeric_casts, trivial_numeric_casts,
rustdoc, // rustdoc,
missing_debug_implementations, missing_debug_implementations,
missing_copy_implementations, missing_copy_implementations,
deprecated_in_future, deprecated_in_future,
@ -29,7 +29,8 @@
clippy::cognitive_complexity, clippy::cognitive_complexity,
clippy::must_use_candidate, clippy::must_use_candidate,
clippy::missing_errors_doc, clippy::missing_errors_doc,
clippy::as_conversions clippy::as_conversions,
missing_doc_code_examples
)] )]
pub mod builtins; pub mod builtins;

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

@ -1,23 +1,103 @@
//! This module implements the `Const` structure, which represents the primitive values in JavaScript.
//!
//! More information:
//! - [ECMAScript reference][spec]
//! - [MDN documentation][mdn]
//!
//! [spec]: https://tc39.es/ecma262/#sec-primary-expression-literals
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals
use gc_derive::{Finalize, Trace}; use gc_derive::{Finalize, Trace};
use std::fmt::{Display, Formatter, Result}; use std::fmt::{Display, Formatter, Result};
#[cfg(feature = "serde-ast")] #[cfg(feature = "serde-ast")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// A Javascript Constant.
/// Literals represent values in JavaScript.
///
/// These are fixed values **not variables** that you literally provide in your script.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-primary-expression-literals
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals
#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)] #[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub enum Const { pub enum Const {
/// A UTF-8 string, such as `"Hello, world"` /// A string literal is zero or more characters enclosed in double (`"`) or single (`'`) quotation marks.
///
/// A string must be delimited by quotation marks of the same type (that is, either both single quotation marks, or both double quotation marks).
/// You can call any of the String object's methods on a string literal value.
/// JavaScript automatically converts the string literal to a temporary String object,
/// calls the method, then discards the temporary String object.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-string-value
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#String_literals
String(String), String(String),
// A 64-bit floating-point number, such as `3.1415`
/// A floating-point number literal.
///
/// The exponent part is an "`e`" or "`E`" followed by an integer, which can be signed (preceded by "`+`" or "`-`").
/// A floating-point literal must have at least one digit, and either a decimal point or "`e`" (or "`E`").
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-number-value
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Floating-point_literals
Num(f64), Num(f64),
// A 32-bit integer, such as `42`
/// Integer types can be expressed in decimal (base 10), hexadecimal (base 16), octal (base 8) and binary (base 2).
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-number-value
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Numeric_literals
Int(i32), Int(i32),
// A boolean, which is either `true` or `false` and is used to check if criteria are met
/// The Boolean type has two literal values: `true` and `false`.
///
/// The Boolean object is a wrapper around the primitive Boolean data type.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-boolean-value
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Boolean_literals
Bool(bool), Bool(bool),
// The `null` value, which represents a non-existant value
/// In JavaScript, `null` is marked as one of the primitive values, cause it's behaviour is seemingly primitive.
///
/// In computer science, a null value represents a reference that points,
/// generally intentionally, to a nonexistent or invalid object or address.
/// The meaning of a null reference varies among language implementations.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-null-value
/// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/null
Null, Null,
// The `undefined` value, which represents a field or index that doesn't exist
/// The `undefined` is a primitive value automatically assigned to variables that have just been declared, or to formal arguments for which there are no actual arguments.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-undefined
/// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/undefined
Undefined, Undefined,
} }

418
boa/src/syntax/ast/keyword.rs

@ -1,3 +1,12 @@
//! This module implements the `Keyword` structure, which represents reserved words of the JavaScript language.
//!
//! More information:
//! - [ECMAScript reference][spec]
//! - [MDN documentation][mdn]
//!
//! [spec]: https://www.ecma-international.org/ecma-262/#sec-keywords
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords
use std::{ use std::{
error, error,
fmt::{Display, Error, Formatter}, fmt::{Display, Error, Formatter},
@ -7,83 +16,420 @@ use std::{
#[cfg(feature = "serde-ast")] #[cfg(feature = "serde-ast")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// A Javascript Keyword /// Keywords are tokens that have special meaning in JavaScript.
///
/// In JavaScript you cannot use these reserved words as variables, labels, or function names.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
/// ///
/// As specificed by <https://www.ecma-international.org/ecma-262/#sec-keywords> /// [spec]: https://www.ecma-international.org/ecma-262/#sec-keywords
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords
#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))]
#[derive(Clone, Copy, PartialEq, Debug)] #[derive(Clone, Copy, PartialEq, Debug)]
pub enum Keyword { pub enum Keyword {
/// The `await` keyword /// The `await` keyword.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-AwaitExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await
Await, Await,
/// The `break` keyword
/// The `break` keyword.
///
/// More information:
/// - [break `Node` documentation][node]
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-BreakStatement
/// [node]: ../node/enum.Node.html#variant.Break
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/break
Break, Break,
/// The `case` keyword
/// The `case` keyword.
///
/// More information:
/// - [switch `Node` documentation][node]
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-CaseClause
/// [node]: ../node/enum.Node.html#variant.Switch
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch
Case, Case,
/// The `catch` keyword
/// The `catch` keyword.
///
/// More information:
/// - [try `Node` documentation][node]
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-Catch
/// [node]: ../node/enum.Node.html#variant.Try
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch
Catch, Catch,
/// The `class` keyword, which is reserved for future use
/// The `class` keyword.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-ClassDeclaration
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/class
Class, Class,
/// The `continue` keyword
/// The `continue` keyword.
///
/// More information:
/// - [continue `Node` documentation][node]
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-ContinueStatement
/// [node]: ../node/enum.Node.html#variant.Continue
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/continue
Continue, Continue,
/// The `const` keyword
/// The `const` keyword.
///
/// More information:
/// - [const `Node` documentation][node]
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-let-and-const-declarations
/// [node]: ../node/enum.Node.html#variant.ConstDecl
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const
Const, Const,
/// The `debugger` keyword
/// The `debugger` keyword.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-debugger-statement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger
Debugger, Debugger,
/// The `default` keyword
/// The `default` keyword.
///
/// More information:
/// - [switch `Node` documentation][node]
/// - [ECMAScript reference default clause][spec-clause]
/// - [ECMAScript reference default export][spec-export]
/// - [MDN documentation][mdn]
///
/// [node]: ../node/enum.Node.html#variant.Switch
/// [spec-clause]: https://tc39.es/ecma262/#prod-DefaultClause
/// [spec-export]: https://tc39.es/ecma262/#prod-ImportedDefaultBinding
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/default
Default, Default,
/// The `delete` keyword
/// The `delete` keyword.
///
/// More information:
/// - [delete `UnaryOp` documentation][unary]
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-delete-operator
/// [unary]: ../op/enum.UnaryOp.html#variant.Delete
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete
Delete, Delete,
/// The `do` keyword
/// The `do` keyword.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-do-while-statement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/do...while
Do, Do,
/// The `else` keyword
/// The `else` keyword.
///
/// More information:
/// - [if `Node` documentation][node]
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [node]: ../node/enum.Node.html#variant.If
/// [spec]: https://tc39.es/ecma262/#prod-IfStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else
Else, Else,
/// The `enum` keyword
/// The `enum` keyword.
///
/// Future reserved keyword.
Enum, Enum,
/// The `export` keyword
/// The `export` keyword.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-exports
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export
Export, Export,
/// The `extends` keyword
/// The `extends` keyword.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-ClassHeritage
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/extends
Extends, Extends,
/// The `finally` keyword
/// The `finally` keyword.
///
/// More information:
/// - [try `Node` documentation][node]
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [node]: ../node/enum.Node.html#variant.Try
/// [spec]: https://tc39.es/ecma262/#prod-Finally
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch
Finally, Finally,
/// The `for` keyword
/// The `for` keyword.
///
/// More information:
/// - [for loop `Node` documentation][node]
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [node]: ../node/enum.Node.html#variant.ForLoop
/// [spec]: https://tc39.es/ecma262/#prod-ForDeclaration
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for
For, For,
/// The `function` keyword
/// The `function` keyword.
///
/// More information:
/// - [function `Node` documentation][node]
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [node]: ../node/enum.Node.html#variant.FunctionDecl
/// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-function
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function
Function, Function,
/// The `if` keyword
/// The `if` keyword.
///
/// More information:
/// - [if `Node` documentation][node]
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [node]: ../node/enum.Node.html#variant.If
/// [spec]: https://tc39.es/ecma262/#prod-IfStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else
If, If,
/// The `in` keyword
/// The `in` keyword.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in
In, In,
/// The `instanceof` keyword
/// The `instanceof` keyword.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-instanceofoperator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof
InstanceOf, InstanceOf,
/// The `import` keyword
/// The `import` keyword.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-imports
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
Import, Import,
/// The `let` keyword
/// The `let` keyword.
///
/// More information:
/// - [let `Node` documentation][node]
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [node]: ../node/enum.Node.html#variant.LetDecl
/// [spec]: https://tc39.es/ecma262/#sec-let-and-const-declarations
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
Let, Let,
/// The `new` keyword
/// The `new` keyword.
///
/// More information:
/// - [new `Node` documentation][node]
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [node]: ../node/enum.Node.html#variant.New
/// [spec]: https://tc39.es/ecma262/#prod-NewExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new
New, New,
/// The `return` keyword /// The `return` keyword
///
/// More information:
/// - [return `Node` documentation][node]
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [node]: ../node/enum.Node.html#variant.Return
/// [spec]: https://tc39.es/ecma262/#prod-ReturnStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/return
Return, Return,
/// The `super` keyword /// The `super` keyword
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-super-keyword
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super
Super, Super,
/// The `switch` keyword
/// The `switch` keyword.
///
/// More information:
/// - [switch `Node` documentation][node]
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [node]: ../node/enum.Node.html#variant.Switch
/// [spec]: https://tc39.es/ecma262/#prod-SwitchStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch
Switch, Switch,
/// The `this` keyword
/// The `this` keyword.
///
/// More information:
/// - [this `Node` documentation][node]
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [node]: ../node/enum.Node.html#variant.This
/// [spec]: https://tc39.es/ecma262/#sec-this-keyword
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this
This, This,
/// The `throw` keyword
/// The `throw` keyword.
///
/// More information:
/// - [throw `Node` documentation][node]
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [node]: ../node/enum.Node.html#variant.Throw
/// [spec]: https://tc39.es/ecma262/#prod-ArrowFunction
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
Throw, Throw,
/// The `try` keyword
/// The `try` keyword.
///
/// More information:
/// - [try `Node` documentation][node]
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [node]: ../node/enum.Node.html#variant.Try
/// [spec]: https://tc39.es/ecma262/#prod-TryStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch
Try, Try,
/// The `typeof` keyword
/// The `typeof` keyword.
///
/// More information:
/// - [typeof `UnaryOp` documentation][unary]
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [unary]: ../op/enum.UnaryOp.html#variant.TypeOf
/// [spec]: https://tc39.es/ecma262/#sec-typeof-operator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof
TypeOf, TypeOf,
/// The `var` keyword
/// The `var` keyword.
///
/// More information:
/// - [var `Node` documentation][node]
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [node]: ../node/enum.Node.html#variant.VarDecl
/// [spec]: https://tc39.es/ecma262/#prod-VariableStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var
Var, Var,
/// The `void` keyword
/// The `void` keyword.
///
/// More information:
/// - [void `UnaryOp` documentation][unary]
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [unary]: ../op/enum.UnaryOp.html#variant.Void
/// [spec]: https://tc39.es/ecma262/#sec-void-operator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void
Void, Void,
/// The `while` keyword
/// The `while` keyword.
///
/// More information:
/// - [while `Node` documentation][node]
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [node]: ../node/enum.Node.html#variant.While
/// [spec]: https://tc39.es/ecma262/#prod-grammar-notation-WhileStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/while
While, While,
/// The `with` keyword
/// The `with` keyword.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-WithStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with
With, With,
/// The 'yield' keyword
/// The 'yield' keyword.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-YieldExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield
Yield, Yield,
} }

2
boa/src/syntax/ast/mod.rs

@ -1,3 +1,5 @@
//! The Javascript Abstract Syntax Tree.
pub mod constant; pub mod constant;
pub mod keyword; pub mod keyword;
pub mod node; pub mod node;

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

@ -1,3 +1,5 @@
//! This module implements the `Node` structure, which composes the AST.
use crate::syntax::ast::{ use crate::syntax::ast::{
constant::Const, constant::Const,
op::{BinOp, Operator, UnaryOp}, op::{BinOp, Operator, UnaryOp},
@ -12,85 +14,482 @@ use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)] #[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub enum Node { pub enum Node {
/// Create an array with items inside. /// An array is an ordered collection of data (either primitive or object depending upon the language).
///
/// Arrays are used to store multiple values in a single variable.
/// This is compared to a variable that can store only one value.
///
/// Each item in an array has a number attached to it, called a numeric index, that allows you to access it.
/// In JavaScript, arrays start at index zero and can be manipulated with various methods.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-ArrayLiteral
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
ArrayDecl(Vec<Node>), ArrayDecl(Vec<Node>),
/// Create an arrow function with the given arguments and internal AST node.
/// An arrow function expression is a syntactically compact alternative to a regular function expression.
///
/// Arrow function expressions are ill suited as methods, and they cannot be used as constructors.
/// Arrow functions cannot be used as constructors and will throw an error when used with new.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-ArrowFunction
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
ArrowFunctionDecl(Vec<FormalParameter>, Box<Node>), ArrowFunctionDecl(Vec<FormalParameter>, Box<Node>),
/// Assign an AST node result to an AST node.
/// An assignment operator assigns a value to its left operand based on the value of its right operand.
///
/// Assignment operator (`=`), assigns the value of its right operand to its left operand.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators
Assign(Box<Node>, Box<Node>), Assign(Box<Node>, Box<Node>),
/// Run an operation between 2 AST nodes.
/// Binary operators requires two operands, one before the operator and one after the operator.
///
/// More information:
/// - [MDN documentation][mdn]
///
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Operators
BinOp(BinOp, Box<Node>, Box<Node>), BinOp(BinOp, Box<Node>, Box<Node>),
/// Run several AST nodes from top-to-bottom.
/// A `block` statement (or compound statement in other languages) is used to group zero or more statements.
///
/// The block statement is often called compound statement in other languages.
/// It allows you to use multiple statements where JavaScript expects only one statement.
/// Combining statements into blocks is a common practice in JavaScript. The opposite behavior is possible using an empty statement,
/// where you provide no statement, although one is required.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-BlockStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/block
Block(Vec<Node>), Block(Vec<Node>),
/// Break statement with an optional label.
/// The `break` statement terminates the current loop, switch, or label statement and transfers program control to the statement following the terminated statement.
///
/// The break statement includes an optional label that allows the program to break out of a labeled statement.
/// The break statement needs to be nested within the referenced label. The labeled statement can be any block statement;
/// it does not have to be preceded by a loop statement.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-BreakStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/break
Break(Option<String>), Break(Option<String>),
/// Call a function with some values.
/// Calling the function actually performs the specified actions with the indicated parameters.
///
/// Defining a function does not execute it. Defining it simply names the function and specifies what to do when the function is called.
/// Functions must be in scope when they are called, but the function declaration can be hoisted
/// The scope of a function is the function in which it is declared (or the entire program, if it is declared at the top level).
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-CallExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions#Calling_functions
Call(Box<Node>, Vec<Node>), Call(Box<Node>, Vec<Node>),
/// Conditional Operator (`{condition} ? {if true} : {if false}`).
/// The `conditional` (ternary) operator is the only JavaScript operator that takes three operands.
///
/// This operator is the only JavaScript operator that takes three operands: a condition followed by a question mark (`?`),
/// then an expression to execute `if` the condition is truthy followed by a colon (`:`), and finally the expression to execute if the condition is `falsy`.
/// This operator is frequently used as a shortcut for the `if` statement.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-ConditionalExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals
ConditionalOp(Box<Node>, Box<Node>, Box<Node>), ConditionalOp(Box<Node>, Box<Node>, Box<Node>),
/// Make a constant value.
/// Literals represent values in JavaScript.
///
/// These are fixed values not variables that you literally provide in your script.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-primary-expression-literals
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals
Const(Const), Const(Const),
/// Const declaration.
/// The `const` statements are block-scoped, much like variables defined using the `let` keyword.
///
/// This declaration creates a constant whose scope can be either global or local to the block in which it is declared.
/// Global constants do not become properties of the window object, unlike var variables.
///
/// An initializer for a constant is required. You must specify its value in the same statement in which it's declared.
/// (This makes sense, given that it can't be changed later.)
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-let-and-const-declarations
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const
/// [identifier]: https://developer.mozilla.org/en-US/docs/Glossary/identifier
/// [expression]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Expressions
ConstDecl(Vec<(String, Node)>), ConstDecl(Vec<(String, Node)>),
/// Continue with an optional label.
/// The `continue` statement terminates execution of the statements in the current iteration of the current or labeled loop,
/// and continues execution of the loop with the next iteration.
///
/// The continue statement can include an optional label that allows the program to jump to the next iteration of a labeled
/// loop statement instead of the current loop. In this case, the continue statement needs to be nested within this labeled statement.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-ContinueStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/continue
Continue(Option<String>), Continue(Option<String>),
/// do [body] while [cond]
/// The `do...while` statement creates a loop that executes a specified statement until the test condition evaluates to false.
///
/// The condition is evaluated after executing the statement, resulting in the specified statement executing at least once.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-do-while-statement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/do...while
DoWhileLoop(Box<Node>, Box<Node>), DoWhileLoop(Box<Node>, Box<Node>),
/// Create a function with the given name, arguments, and internal AST node.
/// The `function` declaration (function statement) defines a function with the specified parameters.
///
/// A function created with a function declaration is a `Function` object and has all the properties, methods and behavior of `Function`.
///
/// A function can also be created using an expression (see function expression).
///
/// By default, functions return undefined. To return any other value, the function must have a return statement that specifies the value to return.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-function
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function
FunctionDecl(Option<String>, Vec<FormalParameter>, Box<Node>), FunctionDecl(Option<String>, Vec<FormalParameter>, Box<Node>),
/// Gets the constant field of a value.
/// This property accessor provides access to an object's properties by using the [dot notation][mdn].
///
/// In the object.property syntax, the property must be a valid JavaScript identifier.
/// (In the ECMAScript standard, the names of properties are technically "IdentifierNames", not "Identifiers",
/// so reserved words can be used but are not recommended).
///
/// One can think of an object as an associative array (a.k.a. map, dictionary, hash, lookup table).
/// The keys in this array are the names of the object's properties.
///
/// It's typical when speaking of an object's properties to make a distinction between properties and methods. However,
/// the property/method distinction is little more than a convention. A method is simply a property that can be called (for example,
/// if it has a reference to a Function instance as its value).
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-property-accessors
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_accessors#Dot_notation
GetConstField(Box<Node>, String), GetConstField(Box<Node>, String),
/// Gets the [field] of a value.
/// This property accessor provides access to an object's properties by using the [bracket notation][mdn].
///
/// In the object[property_name] syntax, the property_name is just a string or [Symbol][symbol]. So, it can be any string, including '1foo', '!bar!', or even ' ' (a space).
///
/// One can think of an object as an associative array (a.k.a. map, dictionary, hash, lookup table).
/// The keys in this array are the names of the object's properties.
///
/// It's typical when speaking of an object's properties to make a distinction between properties and methods. However,
/// the property/method distinction is little more than a convention. A method is simply a property that can be called (for example,
/// if it has a reference to a Function instance as its value).
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-property-accessors
/// [symbol]: https://developer.mozilla.org/en-US/docs/Glossary/Symbol
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_accessors#Bracket_notation
GetField(Box<Node>, Box<Node>), GetField(Box<Node>, Box<Node>),
/// [init], [cond], [step], body
/// The `for` statement creates a loop that consists of three optional expressions.
///
/// A `for` loop repeats until a specified condition evaluates to `false`.
/// The JavaScript for loop is similar to the Java and C for loop.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-ForDeclaration
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for
ForLoop( ForLoop(
Option<Box<Node>>, Option<Box<Node>>,
Option<Box<Node>>, Option<Box<Node>>,
Option<Box<Node>>, Option<Box<Node>>,
Box<Node>, Box<Node>,
), ),
/// Check if a conditional expression is true and run an expression if it is and another expression if it isn't
/// The `if` statement executes a statement if a specified condition is [`truthy`][truthy]. If the condition is [`falsy`][falsy], another statement can be executed.
///
/// Multiple `if...else` statements can be nested to create an else if clause.
///
/// Note that there is no elseif (in one word) keyword in JavaScript.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-IfStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else
/// [truthy]: https://developer.mozilla.org/en-US/docs/Glossary/truthy
/// [falsy]: https://developer.mozilla.org/en-US/docs/Glossary/falsy
/// [expression]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Expressions
If(Box<Node>, Box<Node>, Option<Box<Node>>), If(Box<Node>, Box<Node>, Option<Box<Node>>),
/// Let declaraton
/// The `let` statement declares a block scope local variable, optionally initializing it to a value.
///
///
/// `let` allows you to declare variables that are limited to a scope of a block statement, or expression on which
/// it is used, unlike the `var` keyword, which defines a variable globally, or locally to an entire function regardless of block scope.
///
/// Just like const the `let` does not create properties of the window object when declared globally (in the top-most scope).
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-let-and-const-declarations
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
LetDecl(Vec<(String, Option<Node>)>), LetDecl(Vec<(String, Option<Node>)>),
/// Load a reference to a value, or a function argument
/// An `identifier` is a sequence of characters in the code that identifies a variable, function, or property.
///
/// In JavaScript, identifiers are case-sensitive and can contain Unicode letters, $, _, and digits (0-9), but may not start with a digit.
///
/// An identifier differs from a string in that a string is data, while an identifier is part of the code. In JavaScript, there is no way
/// to convert identifiers to strings, but sometimes it is possible to parse strings into identifiers.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-Identifier
/// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/Identifier
Local(String), Local(String),
/// New
/// The `new` operator lets developers create an instance of a user-defined object type or of one of the built-in object types that has a constructor function.
///
/// The new keyword does the following things:
/// - Creates a blank, plain JavaScript object;
/// - Links (sets the constructor of) this object to another object;
/// - Passes the newly created object from Step 1 as the this context;
/// - Returns this if the function doesn't return its own object.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-NewExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new
New(Box<Node>), New(Box<Node>),
/// Object Declaration
/// Objects in JavaScript may be defined as an unordered collection of related data, of primitive or reference types, in the form of “key: value” pairs.
///
/// Objects can be initialized using `new Object()`, `Object.create()`, or using the literal notation.
///
/// An object initializer is an expression that describes the initialization of an [`Object`][object].
/// Objects consist of properties, which are used to describe an object. Values of object properties can either
/// contain [`primitive`][primitive] data types or other objects.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-ObjectLiteral
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer
/// [object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object
/// [primitive]: https://developer.mozilla.org/en-US/docs/Glossary/primitive
Object(Vec<PropertyDefinition>), Object(Vec<PropertyDefinition>),
/// Return the expression from a function
/// The `return` statement ends function execution and specifies a value to be returned to the function caller.
///
/// Syntax: `return [expression];`
///
/// `expression`:
/// > The expression whose value is to be returned. If omitted, `undefined` is returned instead.
///
/// When a `return` statement is used in a function body, the execution of the function is stopped.
/// If specified, a given value is returned to the function caller.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-ReturnStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/return
Return(Option<Box<Node>>), Return(Option<Box<Node>>),
/// Run blocks whose cases match the expression
/// The `switch` statement evaluates an expression, matching the expression's value to a case clause,
/// and executes statements associated with that case, as well as statements in cases that follow the matching case.
///
/// A `switch` statement first evaluates its expression. It then looks for the first case clause whose expression evaluates
/// to the same value as the result of the input expression (using the strict comparison, `===`) and transfers control to that clause,
/// executing the associated statements. (If multiple cases match the provided value, the first case that matches is selected, even if
/// the cases are not equal to each other.)
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-SwitchStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch
Switch(Box<Node>, Vec<(Node, Vec<Node>)>, Option<Box<Node>>), Switch(Box<Node>, Vec<(Node, Vec<Node>)>, Option<Box<Node>>),
/// `...a` - spread an iterable value
/// The `spread` operator allows an iterable such as an array expression or string to be expanded.
///
/// Syntax: `...x`
///
/// It expands array expressions or strings in places where zero or more arguments (for function calls) or elements (for array literals)
/// are expected, or an object expression to be expanded in places where zero or more key-value pairs (for object literals) are expected.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-SpreadElement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
Spread(Box<Node>), Spread(Box<Node>),
// Similar to Block but without the braces
/// Similar to `Node::Block` but without the braces
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#prod-StatementList
StatementList(Vec<Node>), StatementList(Vec<Node>),
/// Throw a value
/// The `throw` statement throws a user-defined exception.
///
/// Syntax: `throw expression;`
///
/// Execution of the current function will stop (the statements after throw won't be executed),
/// and control will be passed to the first catch block in the call stack. If no catch block exists among
/// caller functions, the program will terminate.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-ThrowStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw
Throw(Box<Node>), Throw(Box<Node>),
/// Return a string representing the type of the given expression
/// The `typeof` operator returns a string indicating the type of the unevaluated operand.
///
/// Syntax: `typeof operand`
///
/// Returns a string indicating the type of the unevaluated operand.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-typeof-operator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof
TypeOf(Box<Node>), TypeOf(Box<Node>),
/// Try / Catch
/// The `try...catch` statement marks a block of statements to try and specifies a response should an exception be thrown.
///
/// The `try` statement consists of a `try`-block, which contains one or more statements. `{}` must always be used,
/// even for single statements. At least one `catch`-block, or a `finally`-block, must be present.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-TryStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch
Try( Try(
Box<Node>, Box<Node>,
Option<Box<Node>>, Option<Box<Node>>,
Option<Box<Node>>, Option<Box<Node>>,
Option<Box<Node>>, Option<Box<Node>>,
), ),
/// The JavaScript `this` keyword refers to the object it belongs to. /// The JavaScript `this` keyword refers to the object it belongs to.
/// ///
/// A property of an execution context (global, function or eval) that, /// A property of an execution context (global, function or eval) that,
/// in non–strict mode, is always a reference to an object and in strict /// in non–strict mode, is always a reference to an object and in strict
/// mode can be any value. /// mode can be any value.
/// ///
/// For more information, please check: <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this> /// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-this-keyword
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this
This, This,
/// Run an operation on a value
/// A unary operation is an operation with only one operand.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-UnaryExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Unary_operators
UnaryOp(UnaryOp, Box<Node>), UnaryOp(UnaryOp, Box<Node>),
/// A variable declaration
/// The `var` statement declares a variable, optionally initializing it to a value.
///
/// var declarations, wherever they occur, are processed before any code is executed. This is called hoisting, and is discussed further below.
///
/// The scope of a variable declared with var is its current execution context, which is either the enclosing function or,
/// for variables declared outside any function, global. If you re-declare a JavaScript variable, it will not lose its value.
///
/// Assigning a value to an undeclared variable implicitly creates it as a global variable
/// (it becomes a property of the global object) when the assignment is executed.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-VariableStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var
VarDecl(Vec<(String, Option<Node>)>), VarDecl(Vec<(String, Option<Node>)>),
/// Repeatedly run an expression while the conditional expression resolves to true
/// The `while` statement creates a loop that executes a specified statement as long as the test condition evaluates to `true`.
///
/// The condition is evaluated before executing the statement.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-grammar-notation-WhileStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/while
WhileLoop(Box<Node>, Box<Node>), WhileLoop(Box<Node>, Box<Node>),
} }
@ -101,6 +500,7 @@ impl Operator for Node {
_ => true, _ => true,
} }
} }
fn get_precedence(&self) -> u64 { fn get_precedence(&self) -> u64 {
match self { match self {
Node::GetField(_, _) | Node::GetConstField(_, _) => 1, Node::GetField(_, _) | Node::GetConstField(_, _) => 1,
@ -335,11 +735,17 @@ fn join_nodes(f: &mut fmt::Formatter<'_>, nodes: &[Node]) -> fmt::Result {
/// ///
/// In the declaration of a function, the parameters must be identifiers, /// In the declaration of a function, the parameters must be identifiers,
/// not any value like numbers, strings, or objects. /// not any value like numbers, strings, or objects.
///```javascript ///```text
///function foo(formalParametar1, formalParametar2) { ///function foo(formalParametar1, formalParametar2) {
///} ///}
///``` ///```
/// For more information, please check <https://tc39.es/ecma262/#prod-FormalParameter> ///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-FormalParameter
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Missing_formal_parameter
#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Trace, Finalize)] #[derive(Clone, Debug, PartialEq, Trace, Finalize)]
pub struct FormalParameter { pub struct FormalParameter {
@ -348,6 +754,13 @@ pub struct FormalParameter {
pub is_rest_param: bool, pub is_rest_param: bool,
} }
/// A sequence of `FormalParameter`.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-FormalParameters
pub type FormalParameters = Vec<FormalParameter>; pub type FormalParameters = Vec<FormalParameter>;
impl FormalParameter { impl FormalParameter {
@ -360,20 +773,119 @@ impl FormalParameter {
} }
} }
/// A JavaScript property is a characteristic of an object, often describing attributes associated with a data structure.
///
/// A property has a name (a string) and a value (primitive, method, or object reference).
/// Note that when we say that "a property holds an object", that is shorthand for "a property holds an object reference".
/// This distinction matters because the original referenced object remains unchanged when you change the property's value.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-PropertyDefinition
/// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/property/JavaScript
// TODO: Support all features: https://tc39.es/ecma262/#prod-PropertyDefinition // TODO: Support all features: https://tc39.es/ecma262/#prod-PropertyDefinition
#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Trace, Finalize)] #[derive(Clone, Debug, PartialEq, Trace, Finalize)]
pub enum PropertyDefinition { pub enum PropertyDefinition {
/// Puts a variable into an object.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-IdentifierReference
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Property_definitions
IdentifierReference(String), IdentifierReference(String),
/// Binds a property name to a JavaScript value.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-PropertyDefinition
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Property_definitions
Property(String, Node), Property(String, Node),
/// A property of an object can also refer to a function or a getter or setter method.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Method_definitions
MethodDefinition(MethodDefinitionKind, String, Node), MethodDefinition(MethodDefinitionKind, String, Node),
/// The Rest/Spread Properties for ECMAScript proposal (stage 4) adds spread properties to object literals.
/// It copies own enumerable properties from a provided object onto a new object.
///
/// Shallow-cloning (excluding `prototype`) or merging objects is now possible using a shorter syntax than `Object.assign()`.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-PropertyDefinition
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Spread_properties
SpreadObject(Node), SpreadObject(Node),
} }
/// Method definition kinds.
///
/// Starting with ECMAScript 2015, a shorter syntax for method definitions on objects initializers is introduced.
/// It is a shorthand for a function assigned to the method's name.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions
#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Trace, Finalize)] #[derive(Clone, Debug, PartialEq, Trace, Finalize)]
pub enum MethodDefinitionKind { pub enum MethodDefinitionKind {
/// The `get` syntax binds an object property to a function that will be called when that property is looked up.
///
/// Sometimes it is desirable to allow access to a property that returns a dynamically computed value,
/// or you may want to reflect the status of an internal variable without requiring the use of explicit method calls.
/// In JavaScript, this can be accomplished with the use of a getter.
///
/// It is not possible to simultaneously have a getter bound to a property and have that property actually hold a value,
/// although it is possible to use a getter and a setter in conjunction to create a type of pseudo-property.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get
Get, Get,
/// The `set` syntax binds an object property to a function to be called when there is an attempt to set that property.
///
/// In JavaScript, a setter can be used to execute a function whenever a specified property is attempted to be changed.
/// Setters are most often used in conjunction with getters to create a type of pseudo-property.
/// It is not possible to simultaneously have a setter on a property that holds an actual value.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set
Set, Set,
/// Starting with ECMAScript 2015, you are able to define own methods in a shorter syntax, similar to the getters and setters.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#Method_definition_syntax
Ordinary, Ordinary,
// TODO: support other method definition kinds, like `Generator`.
} }

689
boa/src/syntax/ast/op.rs

@ -1,3 +1,5 @@
//! This module implements various structure for logic handling.
use gc_derive::{Finalize, Trace}; use gc_derive::{Finalize, Trace};
use std::fmt::{Display, Formatter, Result}; use std::fmt::{Display, Formatter, Result};
@ -16,21 +18,92 @@ pub trait Operator {
} }
} }
/// A numeric operation between 2 values /// Arithmetic operators take numerical values (either literals or variables)
/// as their operands and return a single numerical value.
///
/// More information:
/// - [MDN documentation][mdn]
///
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Arithmetic
#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)] #[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub enum NumOp { pub enum NumOp {
/// `a + b` - Addition /// The addition operator produces the sum of numeric operands or string concatenation.
///
/// Syntax: `x + y`
///
/// More information:
/// - [ECMAScript reference][spec].
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-addition-operator-plus
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Addition
Add, Add,
/// `a - b` - Subtraction
/// The subtraction operator subtracts the two operands, producing their difference.
///
/// Syntax: `x - y`
///
/// More information:
/// - [ECMAScript reference][spec].
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-subtraction-operator-minus
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Subtraction
Sub, Sub,
/// `a / b` - Division
/// The division operator produces the quotient of its operands where the left operand
/// is the dividend and the right operand is the divisor.
///
/// Syntax: `x / y`
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-MultiplicativeOperator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Division
Div, Div,
/// `a * b` - Multiplication
/// The multiplication operator produces the product of the operands.
///
/// Syntax: `x * y`
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-MultiplicativeExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Multiplication
Mul, Mul,
/// `a ** b` - Exponentiation
/// The exponentiation operator returns the result of raising the first operand to
/// the power of the second operand.
///
/// Syntax: `x ** y`
///
/// The exponentiation operator is right-associative. a ** b ** c is equal to a ** (b ** c).
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-exp-operator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Exponentiation
Exp, Exp,
/// `a % b` - Modulus
/// The remainder operator returns the remainder left over when one operand is divided by a second operand.
///
/// Syntax: `x % y`
///
/// The remainder operator always takes the sign of the dividend.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-MultiplicativeOperator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Remainder
Mod, Mod,
} }
@ -51,32 +124,160 @@ impl Display for NumOp {
} }
} }
/// A unary operation on a single value /// A unary operator is one that takes a single operand/argument and performs an operation.
/// ///
/// For more information, please check: <https://tc39.es/ecma262/#prod-UnaryExpression> /// A unary operation is an operation with only one operand. This operand comes either
/// before or after the operator. Unary operators are more efficient than standard JavaScript
/// function calls.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-UnaryExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Unary
#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)] #[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub enum UnaryOp { pub enum UnaryOp {
/// `a++` - increment the value /// The increment operator increments (adds one to) its operand and returns a value.
///
/// Syntax: `++x`
///
/// This operator increments and returns the value after incrementing.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-postfix-increment-operator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Increment
IncrementPost, IncrementPost,
/// `++a` - increment the value
/// The increment operator increments (adds one to) its operand and returns a value.
///
/// Syntax: `x++`
///
/// This operator increments and returns the value before incrementing.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-prefix-increment-operator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Increment
IncrementPre, IncrementPre,
/// `a--` - decrement the value
/// The decrement operator decrements (subtracts one from) its operand and returns a value.
///
/// Syntax: `--x`
///
/// This operator decrements and returns the value before decrementing.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-postfix-decrement-operator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Decrement
DecrementPost, DecrementPost,
/// `--a` - decrement the value
/// The decrement operator decrements (subtracts one from) its operand and returns a value.
///
/// Syntax: `x--`
///
/// This operator decrements the operand and returns the value after decrementing.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-prefix-decrement-operator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Decrement
DecrementPre, DecrementPre,
/// `-a` - negate the value
/// The unary negation operator precedes its operand and negates it.
///
/// Syntax: `-x`
///
/// Converts non-numbers data types to numbers like unary plus,
/// however, it performs an additional operation, negation.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-unary-minus-operator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Unary_negation
Minus, Minus,
/// `+a` - convert to a number
/// The unary plus operator attempts to convert the operand into a number, if it isn't already.
///
/// Syntax: `+x`
///
/// Although unary negation (`-`) also can convert non-numbers, unary plus is the fastest and preferred
/// way of converting something into a number, because it does not perform any other operations on the number.
/// It can convert `string` representations of integers and floats, as well as the non-string values `true`, `false`, and `null`.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-unary-plus-operator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Unary_plus
Plus, Plus,
/// `!a` - get the opposite of the boolean value
/// Returns `false` if its single operand can be converted to `true`; otherwise, returns `true`.
///
/// Syntax: `!x`
///
/// Boolean values simply get inverted: `!true === false` and `!false === true`.
/// Non-boolean values get converted to boolean values first, then are negated.
/// This means that it is possible to use a couple of NOT operators in series to explicitly
/// force the conversion of any value to the corresponding boolean primitive.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-logical-not-operator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators#Logical_NOT
Not, Not,
/// `~a` - bitwise-not of the value
/// Performs the NOT operator on each bit.
///
/// Syntax: `~x`
///
/// NOT `a` yields the inverted value (or one's complement) of `a`.
/// Bitwise NOTing any number x yields -(x + 1). For example, ~-5 yields 4.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-bitwise-not-operator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_NOT
Tilde, Tilde,
/// `typeof` - Get the type of object
/// The `typeof` operator returns a string indicating the type of the unevaluated operand.
///
/// Syntax: `typeof x` or `typeof(x)`
///
/// The `typeof` is a JavaScript keyword that will return the type of a variable when you call it.
/// You can use this to validate function parameters or check if variables are defined.
/// There are other uses as well.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-typeof-operator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof
TypeOf, TypeOf,
/// The JavaScript `delete` operator removes a property from an object. /// The JavaScript `delete` operator removes a property from an object.
/// ///
/// Syntax: `delete x`
///
/// Unlike what common belief suggests, the delete operator has nothing to do with /// Unlike what common belief suggests, the delete operator has nothing to do with
/// directly freeing memory. Memory management is done indirectly via breaking references. /// directly freeing memory. Memory management is done indirectly via breaking references.
/// If no more references to the same property are held, it is eventually released automatically. /// If no more references to the same property are held, it is eventually released automatically.
@ -86,11 +287,18 @@ pub enum UnaryOp {
/// [non-configurable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cant_delete) /// [non-configurable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cant_delete)
/// property, in which case, `false` is returned in non-strict mode. /// property, in which case, `false` is returned in non-strict mode.
/// ///
/// For more information, please check: <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete> /// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-delete-operator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete
Delete, Delete,
/// The `void` operator evaluates the given `expression` and then returns `undefined`. /// The `void` operator evaluates the given `expression` and then returns `undefined`.
/// ///
/// Syntax: `void x`
///
/// This operator allows evaluating expressions that produce a value into places where an /// This operator allows evaluating expressions that produce a value into places where an
/// expression that evaluates to `undefined` is desired. /// expression that evaluates to `undefined` is desired.
/// The `void` operator is often used merely to obtain the `undefined` primitive value, usually using `void(0)` /// The `void` operator is often used merely to obtain the `undefined` primitive value, usually using `void(0)`
@ -99,7 +307,12 @@ pub enum UnaryOp {
/// When using an [immediately-invoked function expression](https://developer.mozilla.org/en-US/docs/Glossary/IIFE), /// When using an [immediately-invoked function expression](https://developer.mozilla.org/en-US/docs/Glossary/IIFE),
/// `void` can be used to force the function keyword to be treated as an expression instead of a declaration. /// `void` can be used to force the function keyword to be treated as an expression instead of a declaration.
/// ///
/// For more information, please check: <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void> /// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-void-operator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void
Void, Void,
} }
@ -123,21 +336,97 @@ impl Display for UnaryOp {
} }
} }
/// A bitwise operation between 2 values /// A bitwise operator is an operator used to perform bitwise operations
/// on bit patterns or binary numerals that involve the manipulation of individual bits.
///
/// More information:
/// - [MDN documentation][mdn]
///
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Bitwise
#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)] #[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub enum BitOp { pub enum BitOp {
/// `a & b` - Bitwise and /// Performs the AND operation on each pair of bits. a AND b yields 1 only if both a and b are 1.
///
/// Syntax: `x & y`
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-BitwiseANDExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_AND
And, And,
/// `a | b` - Bitwise or
/// Performs the OR operation on each pair of bits. a OR b yields 1 if either a or b is 1.
///
/// Syntax: `x | y`
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-BitwiseORExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_OR
Or, Or,
/// `a ^ b` - Bitwise xor
/// Performs the XOR operation on each pair of bits. a XOR b yields 1 if a and b are different.
///
/// Syntax: `x ^ y`
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-BitwiseXORExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_XOR
Xor, Xor,
/// `a << b` - Bit-shift leftwards
/// This operator shifts the first operand the specified number of bits to the left.
///
/// Syntax: `x << y`
///
/// Excess bits shifted off to the left are discarded. Zero bits are shifted in from the right.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-left-shift-operator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Left_shift
Shl, Shl,
/// `a >> b` - Bit-shift rightrights
/// This operator shifts the first operand the specified number of bits to the right.
///
/// Syntax: `x >> y`
///
/// Excess bits shifted off to the right are discarded. Copies of the leftmost bit
/// are shifted in from the left. Since the new leftmost bit has the same value as
/// the previous leftmost bit, the sign bit (the leftmost bit) does not change.
/// Hence the name "sign-propagating".
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-signed-right-shift-operator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Right_shift
Shr, Shr,
/// `a >>> b` - Zero-fill right shift
/// This operator shifts the first operand the specified number of bits to the right.
///
/// Syntax: `x >>> y`
///
/// Excess bits shifted off to the right are discarded. Zero bits are shifted in
/// from the left. The sign bit becomes 0, so the result is always non-negative.
/// Unlike the other bitwise operators, zero-fill right shift returns an unsigned 32-bit integer.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-unsigned-right-shift-operator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Unsigned_right_shift
UShr, UShr,
} }
@ -158,25 +447,137 @@ impl Display for BitOp {
} }
} }
/// A comparitive operation between 2 values /// A comparison operator compares its operands and returns a logical value based on whether the comparison is true.
///
/// The operands can be numerical, string, logical, or object values. Strings are compared based on standard
/// lexicographical ordering, using Unicode values. In most cases, if the two operands are not of the same type,
/// JavaScript attempts to convert them to an appropriate type for the comparison. This behavior generally results in
/// comparing the operands numerically. The sole exceptions to type conversion within comparisons involve the `===` and `!==`
/// operators, which perform strict equality and inequality comparisons. These operators do not attempt to convert the operands
/// to compatible types before checking equality.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: tc39.es/ecma262/#sec-testing-and-comparison-operations
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Comparison
#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)] #[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub enum CompOp { pub enum CompOp {
/// `a == b` - Equality /// The equality operator converts the operands if they are not of the same type, then applies strict comparison.
///
/// Syntax: `y == y`
///
/// If both operands are objects, then JavaScript compares internal references which are equal when operands
/// refer to the same object in memory.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-abstract-equality-comparison
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Equality
Equal, Equal,
/// `a != b` - Unequality
/// The inequality operator returns true if the operands are not equal.
///
/// Syntax: `x != y`
///
/// If the two operands are not of the same type, JavaScript attempts to convert the operands to
/// an appropriate type for the comparison. If both operands are objects, then JavaScript compares
/// internal references which are not equal when operands refer to different objects in memory.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-EqualityExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Inequality
NotEqual, NotEqual,
/// `a === b` - Strict equality
/// The identity operator returns true if the operands are strictly equal **with no type conversion**.
///
/// Syntax: `x === y`
///
/// Returns `true` if the operands are equal and of the same type.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-strict-equality-comparison
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Identity
StrictEqual, StrictEqual,
/// `a !== b` - Strict unequality
/// The non-identity operator returns true if the operands **are not equal and/or not of the same type**.
///
/// Syntax: `x !== y`
///
/// Returns `true` if the operands are of the same type but not equal, or are of different type.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-EqualityExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Nonidentity>
StrictNotEqual, StrictNotEqual,
/// `a > b` - If `a` is greater than `b`
/// The greater than operator returns true if the left operand is greater than the right operand.
///
/// Syntax: `x > y`
///
/// Returns `true` if the left operand is greater than the right operand.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Greater_than_operator
GreaterThan, GreaterThan,
/// `a >= b` - If `a` is greater than or equal to `b`
/// The greater than or equal operator returns true if the left operand is greater than or equal to the right operand.
///
/// Syntax: `x >= y`
///
/// Returns `true` if the left operand is greater than the right operand.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Greater_than_operator
GreaterThanOrEqual, GreaterThanOrEqual,
/// `a < b` - If `a` is less than `b`
/// The less than operator returns true if the left operand is less than the right operand.
///
/// Syntax: `x < y`
///
/// Returns `true` if the left operand is less than the right operand.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Less_than_operator
LessThan, LessThan,
/// `a <= b` - If `a` is less than or equal to `b`
/// The less than or equal operator returns true if the left operand is less than or equal to the right operand.
///
/// Syntax: `x <= y`
///
/// Returns `true` if the left operand is less than or equal to the right operand.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Less_than_or_equal_operator
LessThanOrEqual, LessThanOrEqual,
} }
@ -199,13 +600,44 @@ impl Display for CompOp {
} }
} }
/// A logical operation between 2 boolean values /// Logical operators are typically used with Boolean (logical) values; when they are, they return a Boolean value.
///
/// However, the `&&` and `||` operators actually return the value of one of the specified operands,
/// so if these operators are used with non-Boolean values, they may return a non-Boolean value.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-binary-logical-operators
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Logical
#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)] #[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub enum LogOp { pub enum LogOp {
/// `a && b` - Logical and /// The logical AND operator returns the value of the first operand if it can be coerced into `false`;
/// otherwise, it returns the second operand.
///
/// Syntax: `x && y`
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-LogicalANDExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators#Logical_AND
And, And,
/// `a || b` - Logical or
/// The logical OR operator returns the value the first operand if it can be coerced into `true`;
/// otherwise, it returns the second operand.
///
/// Syntax: `x || y`
///
/// More information:
/// - [ECMAScript reference](
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-LogicalORExpression)
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators#Logical_OR
Or, Or,
} }
@ -222,19 +654,33 @@ impl Display for LogOp {
} }
} }
/// A binary operation between 2 values /// This represents a binary operation between two values.
#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)] #[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub enum BinOp { pub enum BinOp {
/// Numeric operation /// Numeric operation.
///
/// see: [`NumOp`](enum.NumOp.html)
Num(NumOp), Num(NumOp),
/// Bitwise operation
/// Bitwise operation.
///
/// see: [`BitOp`](enum.BitOp.html).
Bit(BitOp), Bit(BitOp),
/// Comparitive operation
/// Comparitive operation.
///
/// see: [`CompOp`](enum.CompOp.html).
Comp(CompOp), Comp(CompOp),
/// Logical operation
/// Logical operation.
///
/// see: [`LogOp`](enum.LogOp.html).
Log(LogOp), Log(LogOp),
/// Assign operation
/// Assign operation.
///
/// see: [`AssignOp`](enum.AssignOp.html).
Assign(AssignOp), Assign(AssignOp),
} }
@ -282,34 +728,159 @@ impl Display for BinOp {
} }
} }
/// A binary operation between 2 values /// An assignment operator assigns a value to its left operand based on the value of its right operand.
///
/// The simple assignment operator is equal (`=`), which assigns the value of its right operand to its
/// left operand. That is, `x = y` assigns the value of `y to x`.
///
/// There are also compound assignment operators that are shorthand for the operations
/// ///
/// <https://tc39.es/ecma262/#prod-AssignmentOperator> /// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Assignment
#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)] #[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub enum AssignOp { pub enum AssignOp {
/// `a += b` - Add assign /// The addition assignment operator adds the value of the right operand to a variable and assigns the result to the variable.
///
/// Syntax: `x += y`
///
/// The types of the two operands determine the behavior of the addition assignment operator. Addition or concatenation is possible.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Addition_assignment
Add, Add,
/// `a -= b` - Sub assign
/// The subtraction assignment operator subtracts the value of the right operand from a variable and assigns the result to the variable.
///
/// Syntax: `x -= y`
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation](mdn)
///
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Subtraction_assignment
Sub, Sub,
/// `a *= b` - Mul assign
/// The multiplication assignment operator multiplies a variable by the value of the right operand and assigns the result to the variable.
///
/// Syntax: `x *= y`
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Multiplication_assignment
Mul, Mul,
/// `a **= b` - Exponent assign
Exp, /// The division assignment operator divides a variable by the value of the right operand and assigns the result to the variable.
/// `a /= b` - Div assign ///
/// Syntax: `x /= y`
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Division_assignment
Div, Div,
/// `a %= b` - Modulus assign
/// The remainder assignment operator divides a variable by the value of the right operand and assigns the remainder to the variable.
///
/// Syntax: `x %= y`
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Remainder_assignment
Mod, Mod,
/// `a &= b` - Bitwise and assign
/// The exponentiation assignment operator raises the value of a variable to the power of the right operand.
///
/// Syntax: `x ** y`
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Exponentiation_assignment
Exp,
/// The bitwise AND assignment operator uses the binary representation of both operands, does a bitwise AND operation on
/// them and assigns the result to the variable.
///
/// Syntax: `x &= y`
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Bitwise_AND_assignment
And, And,
/// `a |= b` - Bitwise or assign
/// The bitwise OR assignment operator uses the binary representation of both operands, does a bitwise OR operation on
/// them and assigns the result to the variable.
///
/// Syntax: `x |= y`
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Bitwise_OR_assignment
Or, Or,
/// `a ^= b` - Bitwise xor assign
/// The bitwise XOR assignment operator uses the binary representation of both operands, does a bitwise XOR operation on
/// them and assigns the result to the variable.
///
/// Syntax: `x ^= y`
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Bitwise_XOR_assignment
Xor, Xor,
/// `a <<= b` - Left shift assign
/// The left shift assignment operator moves the specified amount of bits to the left and assigns the result to the variable.
///
/// Syntax: `x <<= y`
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Left_shift_assignment
Shl, Shl,
/// `a >>= b` - Right shift assign
/// The right shift assignment operator moves the specified amount of bits to the right and assigns the result to the variable.
///
/// Syntax: `x >>= y`
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Right_shift_assignment
Shr, Shr,
// TODO: Add UShl (unsigned shift left).
} }
impl Display for AssignOp { impl Display for AssignOp {

2
boa/src/syntax/ast/pos.rs

@ -1,3 +1,5 @@
//! This module implements the `Pos` structure, which represents a position in the source code.
#[cfg(feature = "serde-ast")] #[cfg(feature = "serde-ast")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};

14
boa/src/syntax/ast/punc.rs

@ -1,10 +1,22 @@
//! This module implements the `Punctuator`, which represents all punctuators used in JavaScript
//!
//! More information:
//! - [ECMAScript Reference][spec]
//!
//! [spec]: https://tc39.es/ecma262/#prod-Punctuator
use crate::syntax::ast::op::{BinOp, BitOp, CompOp, LogOp, NumOp}; use crate::syntax::ast::op::{BinOp, BitOp, CompOp, LogOp, NumOp};
use std::fmt::{Display, Error, Formatter}; use std::fmt::{Display, Error, Formatter};
#[cfg(feature = "serde-ast")] #[cfg(feature = "serde-ast")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// Punctuation /// The Punctuator enum describes all of the punctuators used in JavaScript.
///
/// More information:
/// - [ECMAScript Reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#prod-Punctuator
#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))]
#[derive(PartialEq, Clone, Copy, Debug)] #[derive(PartialEq, Clone, Copy, Debug)]
pub enum Punctuator { pub enum Punctuator {

51
boa/src/syntax/ast/token.rs

@ -1,16 +1,29 @@
//! This module implements all of the [Token]s used in the JavaScript programing language.
//!
//! More information:
//! - [ECMAScript reference][spec]
//!
//! [spec]: https://tc39.es/ecma262/#sec-tokens
use crate::syntax::ast::{keyword::Keyword, pos::Position, punc::Punctuator}; use crate::syntax::ast::{keyword::Keyword, pos::Position, punc::Punctuator};
use std::fmt::{Debug, Display, Formatter, Result}; use std::fmt::{Debug, Display, Formatter, Result};
#[cfg(feature = "serde-ast")] #[cfg(feature = "serde-ast")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// Represents a token. /// This represents the smallest individual words, phrases, or characters that JavaScript can understand.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-tokens
#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct Token { pub struct Token {
/// The token Data /// The token kind, which contains the actual data of the token.
pub kind: TokenKind, pub kind: TokenKind,
/// Token position from original source code
/// The token position from origina source code.
pub pos: Position, pub pos: Position,
} }
@ -30,6 +43,7 @@ impl Display for Token {
} }
} }
/// A continuous sequence of tokens.
pub struct VecToken(Vec<Token>); pub struct VecToken(Vec<Token>);
impl Debug for VecToken { impl Debug for VecToken {
@ -46,25 +60,38 @@ impl Debug for VecToken {
#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))]
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
pub enum TokenKind { pub enum TokenKind {
/// A boolean literal, which is either `true` or `false` /// A boolean literal, which is either `true` or `false`.
BooleanLiteral(bool), BooleanLiteral(bool),
/// The end of the file
/// The end of the file.
EOF, EOF,
/// An identifier
/// An identifier.
Identifier(String), Identifier(String),
/// A keyword
/// A keyword.
///
/// see: [`Keyword`](../keyword/enum.Keyword.html)
Keyword(Keyword), Keyword(Keyword),
/// A `null` literal
/// A `null` literal.
NullLiteral, NullLiteral,
/// A numeric literal
/// A numeric literal.
NumericLiteral(f64), NumericLiteral(f64),
/// A piece of punctuation /// A piece of punctuation
///
/// see: [`Punctuator`](../punc/enum.Punctuator.html)
Punctuator(Punctuator), Punctuator(Punctuator),
/// A string literal
/// A string literal.
StringLiteral(String), StringLiteral(String),
/// A regular expression, consisting of body and flags
/// A regular expression, consisting of body and flags.
RegularExpressionLiteral(String, String), RegularExpressionLiteral(String, String),
/// Indicates the end of a line \n
/// Indicates the end of a line (`\n`).
LineTerminator, LineTerminator,
} }

44
boa/src/syntax/lexer/mod.rs

@ -17,6 +17,8 @@ use std::{
str::{Chars, FromStr}, str::{Chars, FromStr},
}; };
/// `vop` tests the next token to see if we're on an assign operation of just a plain binary operation.
/// If the next value is not an assignment operation it will pattern match the provided values and return the corresponding token.
macro_rules! vop { macro_rules! vop {
($this:ident, $assign_op:expr, $op:expr) => ({ ($this:ident, $assign_op:expr, $op:expr) => ({
let preview = $this.preview_next().ok_or_else(|| LexerError::new("Could not preview next value"))?; let preview = $this.preview_next().ok_or_else(|| LexerError::new("Could not preview next value"))?;
@ -58,6 +60,7 @@ macro_rules! vop {
} }
} }
/// The `op` macro handles binary operations or assignment operations and converts them into tokens.
macro_rules! op { macro_rules! op {
($this:ident, $assign_op:expr, $op:expr) => ({ ($this:ident, $assign_op:expr, $op:expr) => ({
let punc = vop!($this, $assign_op, $op); let punc = vop!($this, $assign_op, $op);
@ -74,12 +77,18 @@ macro_rules! op {
} }
/// An error that occurred during lexing or compiling of the source input. /// An error that occurred during lexing or compiling of the source input.
///
/// [LexerError] implements [fmt::Display] so you just display this value as an error
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct LexerError { pub struct LexerError {
/// details will be displayed when a LexerError occurs
details: String, details: String,
} }
impl LexerError { impl LexerError {
/// Create a new LexerError struct
///
/// * `msg` - The message to show when LexerError is displayed
fn new(msg: &str) -> Self { fn new(msg: &str) -> Self {
Self { Self {
details: msg.to_string(), details: msg.to_string(),
@ -107,13 +116,15 @@ impl error::Error for LexerError {
/// A lexical analyzer for JavaScript source code /// A lexical analyzer for JavaScript source code
#[derive(Debug)] #[derive(Debug)]
pub struct Lexer<'a> { pub struct Lexer<'a> {
// The list fo tokens generated so far /// The list of tokens generated so far.
///
/// This field is public so you can use them once lexing has finished.
pub tokens: Vec<Token>, pub tokens: Vec<Token>,
// The current line number in the script /// The current line number in the script
line_number: u64, line_number: u64,
// the current column number in the script /// the current column number in the script
column_number: u64, column_number: u64,
// The full string /// The full Peekable buffer, an array of [Char]s
buffer: Peekable<Chars<'a>>, buffer: Peekable<Chars<'a>>,
} }
@ -124,13 +135,6 @@ impl<'a> Lexer<'a> {
/// ///
/// * `buffer` - A string slice that holds the source code. /// * `buffer` - A string slice that holds the source code.
/// The buffer needs to have a lifetime as long as the Lexer instance itself /// The buffer needs to have a lifetime as long as the Lexer instance itself
///
/// # Example
///
/// ```rust,no_run
/// let buffer = std::fs::read_to_string("yourSourceCode.js").unwrap();
/// let lexer = boa::syntax::lexer::Lexer::new(&buffer);
/// ```
pub fn new(buffer: &'a str) -> Lexer<'a> { pub fn new(buffer: &'a str) -> Lexer<'a> {
Lexer { Lexer {
tokens: Vec::new(), tokens: Vec::new(),
@ -140,7 +144,7 @@ impl<'a> Lexer<'a> {
} }
} }
/// Push tokens onto the token queue /// Push a token onto the token queue.
fn push_token(&mut self, tk: TokenKind) { fn push_token(&mut self, tk: TokenKind) {
self.tokens self.tokens
.push(Token::new(tk, self.line_number, self.column_number)) .push(Token::new(tk, self.line_number, self.column_number))
@ -204,6 +208,7 @@ impl<'a> Lexer<'a> {
result result
} }
/// Utility function for reading integers in different bases
fn read_integer_in_base(&mut self, base: u32, mut buf: String) -> Result<u64, LexerError> { fn read_integer_in_base(&mut self, base: u32, mut buf: String) -> Result<u64, LexerError> {
self.next(); self.next();
while let Some(ch) = self.preview_next() { while let Some(ch) = self.preview_next() {
@ -217,6 +222,9 @@ impl<'a> Lexer<'a> {
.map_err(|_| LexerError::new("Could not convert value to u64")) .map_err(|_| LexerError::new("Could not convert value to u64"))
} }
/// Utility function for checkint the NumericLiteral is not followed by an `IdentifierStart` or `DecimalDigit` character
///
/// More info [ECMAScript Specification](https://tc39.es/ecma262/#sec-literals-numeric-literals)
fn check_after_numeric_literal(&mut self) -> Result<(), LexerError> { fn check_after_numeric_literal(&mut self) -> Result<(), LexerError> {
match self.preview_next() { match self.preview_next() {
Some(ch) Some(ch)
@ -229,6 +237,18 @@ impl<'a> Lexer<'a> {
} }
} }
/// Runs the lexer until completion, returning a [LexerError] if there's a syntax issue, or an empty unit result
///
/// # Example
///
/// ```
/// # use boa::syntax::lexer::{LexerError, Lexer};
/// fn main() -> Result<(), LexerError> {
/// let buffer = String::from("Hello World");
/// let mut lexer = Lexer::new(&buffer);
/// lexer.lex()
/// }
/// ```
pub fn lex(&mut self) -> Result<(), LexerError> { pub fn lex(&mut self) -> Result<(), LexerError> {
loop { loop {
// Check if we've reached the end // Check if we've reached the end

5
boa/src/syntax/mod.rs

@ -1,6 +1,5 @@
/// The Javascript Abstract Syntax Tree //! Syntactical analysis, such as AST, Parsing and Lexing
pub mod ast; pub mod ast;
/// Lexical analysis (tokenizing/lexing).
pub mod lexer; pub mod lexer;
// Parses a sequence of tokens into expressions
pub mod parser; pub mod parser;

Loading…
Cancel
Save