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
## Development documentation
You can check the internal development docs at jasonwilliams.github.io/boa/doc
## Benchmarks
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)]
mod tests;
@ -14,6 +25,7 @@ use gc::Gc;
use std::borrow::Borrow;
use std::cmp::{max, min};
/// Creates a new `Array` instance.
pub(crate) fn new_array(interpreter: &Interpreter) -> ResultValue {
let array = ValueData::new_obj(Some(
&interpreter
@ -36,8 +48,10 @@ pub(crate) fn new_array(interpreter: &Interpreter) -> ResultValue {
Ok(array)
}
/// Utility function for creating array objects: `array_obj` can be any array with
/// prototype already set (it will be wiped and recreated from `array_contents`)
/// Utility function for creating array objects.
///
/// `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 {
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())
}
/// Array.isArray ( arg )
/// `Array.isArray( arg )`
///
/// 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.
/// <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 {
let value_true = Gc::new(ValueData::Boolean(true));
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
/// array containing the array elements of the object followed by the array
/// 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 {
if args.is_empty() {
// 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)
}
/// Array.prototype.push ( ...items )
/// `Array.prototype.push( ...items )`
///
/// 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
/// 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 {
let new_array = add_to_array_object(this, args)?;
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.
/// <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 {
let curr_length: 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)
}
/// Array.prototype.forEach ( callbackFn [ , thisArg ] )
/// `Array.prototype.forEach( callbackFn [ , thisArg ] )`
///
/// 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 {
if args.is_empty() {
return Err(to_value(
@ -233,12 +276,18 @@ pub fn for_each(this: &Value, args: &[Value], interpreter: &mut Interpreter) ->
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
/// then concatenated, separated by occurrences of the separator. If no
/// 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 {
let separator = if args.is_empty() {
String::from(",")
@ -257,12 +306,18 @@ pub fn join(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
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
/// its this value be an Array object. Therefore it can be transferred to
/// 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 {
let method_name = "join";
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))
}
/// Array.prototype.reverse ( )
/// `Array.prototype.reverse()`
///
/// The elements of the array are rearranged so as to reverse their order.
/// 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)]
pub fn reverse(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let len: i32 =
@ -328,10 +389,16 @@ pub fn reverse(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(this.clone())
}
/// Array.prototype.shift ( )
/// `Array.prototype.shift()`
///
/// 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 {
let len: 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)
}
/// Array.prototype.unshift ( ...items )
/// `Array.prototype.unshift( ...items )`
///
/// 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
/// 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 {
let len: 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))
}
/// Array.prototype.every ( callback, [ thisArg ] )
/// `Array.prototype.every( callback, [ thisArg ] )`
///
/// The every method executes the provided callback function once for each
/// 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
/// 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 {
if args.is_empty() {
return Err(to_value(
@ -436,11 +515,17 @@ pub fn every(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Res
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
/// 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 {
if args.is_empty() {
return Err(to_value(
@ -471,7 +556,7 @@ pub fn map(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Resul
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,
@ -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
/// from the end of the array to compute fromIndex. If the computed index is less than 0,
/// 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 {
// If no arguments, return -1. Not described in spec, but is what chrome does.
if args.is_empty() {
@ -521,7 +612,7 @@ pub fn index_of(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValu
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
@ -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,
/// 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.
/// <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 {
// If no arguments, return -1. Not described in spec, but is what chrome does.
if args.is_empty() {
@ -570,12 +667,18 @@ pub fn last_index_of(this: &Value, args: &[Value], _: &mut Interpreter) -> Resul
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
/// until the callback returns a truthy value. If so, find immediately returns the value
/// 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 {
if args.is_empty() {
return Err(to_value(
@ -600,12 +703,18 @@ pub fn find(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Resu
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.
/// 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.
/// <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 {
if args.is_empty() {
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))))
}
/// 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)
/// to an end index (default array length) with a static value. It returns the modified array
/// <https://tc39.es/ecma262/#sec-array.prototype.fill>
/// to an end index (default array length) with a static value. It returns the modified array.
///
/// 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 {
let len: i32 = from_value(this.get_field_slice("length")).expect("Could not get argument");
let default_value = undefined();
@ -671,6 +786,16 @@ pub fn fill(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
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 {
let search_element = args
.get(0)
@ -691,14 +816,20 @@ pub fn includes_value(this: &Value, args: &[Value], _: &mut Interpreter) -> Resu
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
/// 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
/// 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.
/// <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 {
let new_array = new_array(interpreter)?;
let len: i32 =
@ -737,11 +868,17 @@ pub fn slice(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Res
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
/// array is constructed for every value whose callback returned a truthy value
/// <https://tc39.es/ecma262/#sec-array.prototype.filter>
/// array is constructed for every value whose callback returned a truthy value.
///
/// 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 {
if args.is_empty() {
return Err(to_value(
@ -786,7 +923,13 @@ pub fn filter(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Re
/// in the array. Otherwise, false.
///
/// 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 {
if args.is_empty() {
return Err(to_value(
@ -816,7 +959,7 @@ pub fn some(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Resu
Ok(to_value(false))
}
/// Create a new `Array` object
/// Create a new `Array` object.
pub fn create_constructor(global: &Value) -> Value {
// Create Constructor
let object_prototype = global.get_field_slice("Object").get_field_slice(PROTOTYPE);

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)]
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 {
let b = this_boolean_value(this);
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 {
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 {
match *value.deref().borrow() {
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)]
#[cfg(test)]
@ -14,6 +27,7 @@ use crate::{
use gc::Gc;
use std::{collections::HashMap, time::SystemTime};
/// This is the internal console object state.
#[derive(Debug, Default)]
pub struct ConsoleState {
count_map: HashMap<String, u32>,
@ -33,6 +47,7 @@ impl ConsoleState {
impl InternalState for ConsoleState {}
/// This represents the different types of log messages.
#[derive(Debug)]
pub enum LogMessage {
Log(String),
@ -41,12 +56,14 @@ pub enum LogMessage {
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> {
args.get(index)
.cloned()
.map(|s| from_value::<T>(s).expect("Convert error"))
}
/// Helper function for logging messages.
pub fn logger(msg: LogMessage, console_state: &ConsoleState) {
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 {
let target = get_arg_at_index::<String>(data, 0).unwrap_or_default();
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
/// 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 {
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.
///
/// 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 {
this.with_internal_state_mut(|state: &mut ConsoleState| {
state.groups.clear();
@ -166,7 +194,12 @@ pub fn clear(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
///
/// 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 {
this.with_internal_state_ref(|state| logger(LogMessage::Log(formatter(&args[..])), state));
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.
///
/// 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 {
this.with_internal_state_ref(|state| logger(LogMessage::Error(formatter(&args[..])), state));
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.
///
/// 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 {
this.with_internal_state_ref(|state| logger(LogMessage::Info(formatter(&args[..])), state));
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.
///
/// 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 {
this.with_internal_state_ref(|state| logger(LogMessage::Log(formatter(&args[..])), state));
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.
///
/// 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 {
if !args.is_empty() {
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.
///
/// 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 {
this.with_internal_state_ref(|state| logger(LogMessage::Warn(formatter(&args[..])), state));
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.
///
/// 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 {
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.
///
/// 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 {
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.
///
/// 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 {
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.
///
/// 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 {
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.
///
/// 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 {
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.
///
/// 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 {
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.
///
/// 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 {
this.with_internal_state_mut(|state: &mut ConsoleState| {
state.groups.pop();
@ -386,7 +479,12 @@ pub fn group_end(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue
///
/// 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 {
this.with_internal_state_mut(|state: &mut ConsoleState| {
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::{
builtins::{
function::NativeFunctionData,
@ -8,7 +20,7 @@ use crate::{
};
use gc::Gc;
/// Create a new error
/// Create a new error object.
pub fn make_error(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
if !args.is_empty() {
this.set_field_slice(
@ -25,13 +37,24 @@ pub fn make_error(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultVa
this.set_kind(ObjectKind::Error);
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 {
let name = this.get_field_slice("name");
let message = this.get_field_slice("message");
Ok(to_value(format!("{}: {}", name, message)))
}
/// Create a new `Error` object
/// Create a new `Error` object.
pub fn _create(global: &Value) -> Value {
let prototype = ValueData::new_obj(Some(global));
prototype.set_field_slice("message", to_value(""));
@ -41,7 +64,8 @@ pub fn _create(global: &Value) -> Value {
error.set_field_slice(PROTOTYPE, prototype);
error
}
/// Initialise the global object with the `Error` object
/// Initialise the global object with the `Error` object.
pub fn init(global: &Value) {
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)]
mod tests;
@ -18,12 +29,11 @@ use std::ops::Deref;
/// fn(this, arguments, ctx)
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
/// <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
/// A Javascript function
#[derive(Trace, Finalize, Debug, Clone)]
pub enum Function {
/// A native javascript function
@ -32,7 +42,7 @@ pub enum Function {
RegularFunc(RegularFunction),
}
/// Represents a regular javascript function in memory
/// Represents a regular javascript function in memory.
#[derive(Trace, Finalize, Debug, Clone)]
pub struct RegularFunction {
/// 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.
//!
//! 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
//! 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 serde_json::{self, Value as JSONValue};
/// Parse a JSON string into a Javascript object
/// <https://tc39.es/ecma262/#sec-json.parse>
#[cfg(test)]
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 {
match serde_json::from_str::<JSONValue>(
&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 {
let obj = args.get(0).expect("cannot get argument for JSON.stringify");
let json = obj.to_json().to_string();
Ok(to_value(json))
}
/// Create a new `JSON` object
/// Create a new `JSON` object.
pub fn create_constructor(global: &Value) -> Value {
let json = ValueData::new_obj(Some(global));
@ -49,25 +78,3 @@ pub fn create_constructor(global: &Value) -> Value {
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::{
builtins::{
function::NativeFunctionData,
@ -11,7 +24,14 @@ use std::f64;
#[cfg(test)]
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 {
Ok(to_value(if args.is_empty() {
f64::NAN
@ -21,7 +41,15 @@ pub fn abs(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.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 {
Ok(to_value(if args.is_empty() {
f64::NAN
@ -31,7 +59,15 @@ pub fn acos(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.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 {
Ok(to_value(if args.is_empty() {
f64::NAN
@ -41,7 +77,15 @@ pub fn acosh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.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 {
Ok(to_value(if args.is_empty() {
f64::NAN
@ -51,7 +95,15 @@ pub fn asin(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.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 {
Ok(to_value(if args.is_empty() {
f64::NAN
@ -61,7 +113,15 @@ pub fn asinh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.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 {
Ok(to_value(if args.is_empty() {
f64::NAN
@ -71,7 +131,15 @@ pub fn atan(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.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 {
Ok(to_value(if args.is_empty() {
f64::NAN
@ -81,7 +149,15 @@ pub fn atanh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.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 {
Ok(to_value(if args.is_empty() {
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())
}))
}
/// 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 {
Ok(to_value(if args.is_empty() {
f64::NAN
@ -101,7 +185,15 @@ pub fn cbrt(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.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 {
Ok(to_value(if args.is_empty() {
f64::NAN
@ -111,7 +203,15 @@ pub fn ceil(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.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 {
Ok(to_value(if args.is_empty() {
f64::NAN
@ -121,7 +221,15 @@ pub fn cos(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.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 {
Ok(to_value(if args.is_empty() {
f64::NAN
@ -131,7 +239,15 @@ pub fn cosh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.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 {
Ok(to_value(if args.is_empty() {
f64::NAN
@ -141,7 +257,15 @@ pub fn exp(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.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 {
Ok(to_value(if args.is_empty() {
f64::NAN
@ -151,7 +275,15 @@ pub fn floor(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.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 {
Ok(to_value(if args.is_empty() {
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 {
Ok(to_value(if args.is_empty() {
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 {
Ok(to_value(if args.is_empty() {
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 {
let mut max = f64::NEG_INFINITY;
for arg in args {
@ -205,7 +361,15 @@ pub fn max(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
}
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 {
let mut max = f64::INFINITY;
for arg in args {
@ -214,7 +378,15 @@ pub fn min(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
}
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 {
Ok(to_value(if args.len() >= 2 {
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
}))
}
/// 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 {
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 {
Ok(to_value(if args.is_empty() {
f64::NAN
@ -240,7 +428,15 @@ pub fn round(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.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 {
Ok(to_value(if args.is_empty() {
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 {
Ok(to_value(if args.is_empty() {
f64::NAN
@ -265,7 +469,15 @@ pub fn sin(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.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 {
Ok(to_value(if args.is_empty() {
f64::NAN
@ -275,7 +487,15 @@ pub fn sinh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.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 {
Ok(to_value(if args.is_empty() {
f64::NAN
@ -295,7 +515,15 @@ pub fn tan(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.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 {
Ok(to_value(if args.is_empty() {
f64::NAN
@ -305,7 +533,15 @@ pub fn tanh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.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 {
Ok(to_value(if args.is_empty() {
f64::NAN
@ -315,6 +551,7 @@ pub fn trunc(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
.trunc()
}))
}
/// Create a new `Math` object
pub fn create_constructor(global: &Value) -> Value {
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.
macro_rules! make_builtin_fn {
($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;
/// the global `Symbol` Object
pub mod symbol;
// The global `Boolean` object
pub mod boolean;
/// The global `console` object
pub mod console;
/// The global `Error` object
pub mod error;
/// The global `Function` object and function value representations
pub mod function;
/// The global `JSON` object
pub mod json;
/// The global `Math` object
pub mod math;
/// The global `Number` object
pub mod number;
/// The global `Object` object
pub mod object;
/// Property, used by `Object`
pub mod property;
/// The global 'RegExp' object
pub mod regexp;
/// The global `String` object
pub mod string;
/// Javascript values, utility methods and conversion between Javascript values and Rust values
pub mod symbol;
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)]
mod tests;
@ -11,9 +26,7 @@ use crate::{
};
use std::{borrow::Borrow, f64, ops::Deref};
/// Helper function: to_number(value: &Value) -> Value
///
/// Converts a Value to a Number.
/// Helper function that converts a Value to a Number.
fn to_number(value: &Value) -> Value {
match *value.deref().borrow() {
ValueData::Boolean(b) => {
@ -35,9 +48,7 @@ fn to_number(value: &Value) -> Value {
}
}
/// Helper function: num_to_exponential(n: f64) -> String
///
/// Formats a float as a ES6-style exponential number string.
/// Helper function that formats a float as a ES6-style exponential number string.
fn num_to_exponential(n: f64) -> String {
match n.abs() {
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 {
let data = match args.get(0) {
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())
}
/// 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 {
let data = match args.get(0) {
Some(ref value) => to_number(value),
@ -69,16 +78,32 @@ pub fn call_number(_this: &Value, args: &[Value], _ctx: &mut Interpreter) -> Res
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 {
let this_num = to_number(this).to_num();
let this_str_num = num_to_exponential(this_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 {
let this_num = to_number(this).to_num();
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))
}
/// 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
/// 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 {
let this_num = to_number(this).to_num();
let this_str_num = format!("{}", this_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 {
let this_num = to_number(this);
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");
}
/// 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 {
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 {
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::{
object::{Object, PROTOTYPE},
property::Property,
@ -7,12 +14,22 @@ use gc::Gc;
use std::borrow::Borrow;
use std::ops::Deref;
/// Here lies the internal methods for ordinary objects.
/// Most objects make use of these methods, including exotic objects like functions.
/// Here lies the internal methods for ordinary objects.
///
/// Most objects make use of these methods, including exotic objects like functions.
/// 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 {
/// 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 {
debug_assert!(Property::is_property_key(val));
let prop = self.get_own_property(val);
@ -32,7 +49,12 @@ pub trait ObjectInternalMethods {
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 {
let val = self.get_internal_slot("extensible");
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 {
self.set_internal_slot("extensible", to_value(false));
true
}
// [[Delete]]
/// Delete property.
fn delete(&mut self, prop_key: &Value) -> bool {
debug_assert!(Property::is_property_key(prop_key));
let desc = self.get_own_property(prop_key);
@ -102,7 +129,7 @@ pub trait ObjectInternalMethods {
Gc::new(ValueData::Undefined)
}
/// [[Set]]
/// [[Set]]
/// <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 {
// [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::{
builtins::{
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.
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)]
pub struct Object {
/// Kind
/// The type of the object.
pub kind: ObjectKind,
/// Internal Slots
/// Intfiernal Slots
pub internal_slots: Box<HashMap<String, Value>>,
/// Properties
pub properties: Box<HashMap<String, Property>>,
@ -38,7 +53,17 @@ pub struct 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 {
debug_assert!(val.is_object() || val.is_null());
let current = self.get_internal_slot(PROTOTYPE);
@ -64,20 +89,22 @@ impl ObjectInternalMethods for Object {
true
}
/// Helper function for property insertion.
fn insert_property(&mut self, name: String, p: Property) {
self.properties.insert(name, p);
}
/// Helper function for property removal.
fn remove_property(&mut self, name: &str) {
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) {
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 {
match self.internal_slots.get(name) {
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. These are 2 separate types and we can't do that here.
/// The specification returns a Property Descriptor or Undefined.
///
/// 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 {
debug_assert!(Property::is_property_key(prop));
// 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)]
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()));
@ -285,9 +324,12 @@ impl 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
pub fn create(proto: Value) -> Object {
let mut obj = Object::default();
@ -298,19 +340,20 @@ impl Object {
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) {
self.internal_slots.insert(name.to_string(), to_value(val));
}
/// Utility function to set a method on this object
/// The native function will live in the `properties` field of the Object
/// Utility function to set a method on this object.
///
/// The native function will live in the `properties` field of the Object.
pub fn set_method(&mut self, name: &str, val: NativeFunctionData) {
self.properties
.insert(name.to_string(), Property::default().value(to_value(val)));
}
/// 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 {
let mut obj = Object {
kind: ObjectKind::Boolean,
@ -325,7 +368,7 @@ impl Object {
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 {
let mut obj = Object {
kind: ObjectKind::Number,
@ -340,7 +383,7 @@ impl Object {
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 {
let mut obj = Object {
kind: ObjectKind::String,
@ -355,7 +398,12 @@ impl Object {
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, ()> {
match *value.deref().borrow() {
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)]
pub enum ObjectKind {
Function,
@ -379,18 +428,18 @@ pub enum ObjectKind {
Number,
}
/// Create a new object
/// Create a new object.
pub fn make_object(_: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
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 {
let obj = args.get(0).expect("Cannot get object");
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 {
let obj = args.get(0).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))
}
/// 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 {
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 {
let prop = if args.is_empty() {
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 {
let object = to_value(make_object as NativeFunctionData);
// 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 gc_derive::{Finalize, Trace};
/// 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)
/// This represents a Javascript Property AKA The Property Descriptor.
///
/// 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.
///
/// 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)]
pub struct Property {
/// If the type of this can be changed and this can be deleted
@ -91,19 +120,32 @@ impl Property {
&& self.enumerable.is_none()
}
/// An accessor Property Descriptor is one that includes any fields named either [[Get]] or [[Set]].
/// <https://tc39.es/ecma262/#sec-isaccessordescriptor>
/// An accessor Property Descriptor is one that includes any fields named either [[Get]] or [[Set]].
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-isaccessordescriptor
pub fn is_accessor_descriptor(&self) -> bool {
self.get.is_some() || self.set.is_some()
}
/// A data Property Descriptor is one that includes any fields named either [[Value]] or [[Writable]].
/// https://tc39.es/ecma262/#sec-isdatadescriptor
/// A data Property Descriptor is one that includes any fields named either [[Value]] or [[Writable]].
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-isdatadescriptor
pub fn is_data_descriptor(&self) -> bool {
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 {
!self.is_accessor_descriptor() && !self.is_data_descriptor()
}
@ -111,7 +153,11 @@ impl Property {
impl Default for 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 {
Self {
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 gc::Gc;
@ -13,30 +24,40 @@ use crate::{
exec::Interpreter,
};
/// The internal representation on a `RegExp` object.
#[derive(Debug)]
struct RegExp {
/// Regex matcher.
matcher: Regex,
/// Update last_index, set if global or sticky flags are set.
use_last_index: bool,
/// String of parsed flags.
flags: String,
/// Flag 's' - dot matches newline characters.
dot_all: bool,
/// Flag 'g'
global: bool,
/// Flag 'i' - ignore case.
ignore_case: bool,
/// Flag 'm' - '^' and '$' match beginning/end of line.
multiline: bool,
/// Flag 'y'
sticky: bool,
/// Flag 'u' - Unicode.
unicode: bool,
}
impl InternalState for RegExp {}
/// Helper function for getting an argument.
fn get_argument<T: FromValue>(args: &[Value], idx: usize) -> Result<T, Value> {
match args.get(idx) {
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())
}
/// `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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.unicode)))
}
/// Helper function.
fn _make_prop(getter: NativeFunctionData) -> Property {
Property::default().get(to_value(getter))
}
/// 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 {
let arg_str = get_argument::<String>(args, 0)?;
let mut last_index =
@ -209,7 +325,18 @@ pub fn test(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
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 {
let arg_str = get_argument::<String>(args, 0)?;
let mut last_index =
@ -250,8 +377,16 @@ pub fn exec(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
result
}
/// RegExp.prototype[Symbol.match]
/// Returns matches of the regular expression against a string
/// `RegExp.prototype[ @@match ]( 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 {
let (matcher, flags) =
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 {
let body = from_value::<String>(this.get_internal_slot("OriginalSource")).map_err(to_value)?;
let flags = this.with_internal_state_ref(|regex: &RegExp| regex.flags.clone());
Ok(to_value(format!("/{}/{}", body, flags)))
}
/// RegExp.prototype[Symbol.matchAll]
/// Returns all matches of the regular expression against a string
/// TODO: it's returning an array, it should return an iterator
/// `RegExp.prototype[ @@matchAll ]( string )`
///
/// 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 {
let matches: Vec<Value> = this.with_internal_state_ref(|regex: &RegExp| {
let mut matches = Vec::new();
@ -319,7 +471,7 @@ pub fn match_all(this: &Value, arg_str: String) -> ResultValue {
Ok(result)
}
/// Create a new `RegExp` object
/// Create a new `RegExp` object.
pub fn create_constructor(global: &Value) -> Value {
// Create constructor function
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)]
mod tests;
@ -20,7 +31,6 @@ use std::{
};
/// 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
pub fn make_string(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
// 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]]
/// 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 {
let arg = match args.get(0) {
Some(v) => v.clone(),
@ -62,10 +73,22 @@ pub fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue
Ok(to_value(format!("{}", primitive_val)))
}
/// Returns a single element String containing the code unit at index pos within the String value
/// 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.
/// <https://tc39.es/ecma262/#sec-string.prototype.charat>
/// `String.prototype.charAt( index )`
///
/// 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.
///
/// 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 {
// 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
@ -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
/// 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.
/// <https://tc39.es/ecma262/#sec-string.prototype.charcodeat>
/// `String.prototype.charCodeAt( index )`
///
/// The `charCodeAt()` method returns an integer between `0` and `65535` representing the UTF-16 code unit at the given index.
///
/// 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 {
// 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
@ -128,9 +161,20 @@ pub fn char_code_at(this: &Value, args: &[Value], ctx: &mut Interpreter) -> Resu
Ok(to_value(f64::from(utf16_val)))
}
/// Returns a String that is the result of concatenating this String and all strings provided as
/// arguments
/// <https://tc39.es/ecma262/#sec-string.prototype.concat>
/// `String.prototype.concat( str1[, ...strN] )`
///
/// 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 {
// 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
@ -144,9 +188,17 @@ pub fn concat(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValu
Ok(to_value(new_str))
}
/// Returns a String that is the result of repeating this String the number of times given by the
/// first argument
/// <https://tc39.es/ecma262/#sec-string.prototype.repeat>
/// `String.prototype.repeat( count )`
///
/// 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 {
// 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
@ -161,9 +213,16 @@ pub fn repeat(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValu
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
/// to but not including character at "end" index
/// <https://tc39.es/ecma262/#sec-string.prototype.slice>
/// `String.prototype.slice( beginIndex [, endIndex] )`
///
/// 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 {
// 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
@ -211,10 +270,16 @@ pub fn slice(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue
Ok(to_value(new_str))
}
/// Returns a Boolean indicating whether the sequence of code units of the
/// "search string" is the same as the corresponding code units of this string
/// starting at index "position"
/// <https://tc39.es/ecma262/#sec-string.prototype.startswith>
/// `String.prototype.startWith( searchString[, position] )`
///
/// The `startsWith()` method determines whether a string begins with the characters of a specified string, returning `true` or `false` as appropriate.
///
/// 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 {
// 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
@ -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
/// "search string" is the same as the corresponding code units of this string
/// starting at position "end position" - length
/// <https://tc39.es/ecma262/#sec-string.prototype.endswith>
/// `String.prototype.endsWith( searchString[, length] )`
///
/// The `endsWith()` method determines whether a string ends with the characters of a specified string, returning `true` or `false` as appropriate.
///
/// 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 {
// 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
@ -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
/// 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
/// assumed, so as to search all of the String.
/// <https://tc39.es/ecma262/#sec-string.prototype.includes>
/// `String.prototype.includes( searchString[, position] )`
///
/// The `includes()` method determines whether one string may be found within another string, returning `true` or `false` as appropriate.
///
/// 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 {
// 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
@ -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 {
// TODO: Support Symbol replacer
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
/// 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
/// returned. If position is undefined, 0 is assumed, so as to search all of the
/// String.
/// <https://tc39.es/ecma262/#sec-string.prototype.includes>
/// `String.prototype.indexOf( searchValue[, fromIndex] )`
///
/// The `indexOf()` method returns the index within the calling `String` object of the first occurrence of the specified value, starting the search at `fromIndex`.
///
/// Returns -1 if the value is not found.
///
/// 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 {
// 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
@ -483,12 +579,18 @@ pub fn index_of(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultVa
Ok(to_value(-1))
}
//// If searchString appears as a substring of the result of converting this
/// 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
/// returned. If position is undefined, the length of the String value is
/// assumed, so as to search all of the String.
/// <https://tc39.es/ecma262/#sec-string.prototype.lastindexof>
/// `String.prototype.lastIndexOf( searchValue[, fromIndex] )`
///
/// The `lastIndexOf()` method returns the index within the calling `String` object of the last occurrence of the specified value, searching backwards from `fromIndex`.
///
/// Returns -1 if the value is not found.
///
/// 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 {
// 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
@ -530,16 +632,24 @@ pub fn last_index_of(this: &Value, args: &[Value], ctx: &mut Interpreter) -> Res
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,
/// in its absence, only the first complete match and its related capturing groups is returned,
/// otherwise null is returned if no match is found.
/// <https://tc39.es/ecma262/#sec-string.prototype.match>
/// `String.prototype.match( regexp )`
///
/// The `match()` method retrieves the result of matching a **string** against a [`regular expression`][regex].
///
/// 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 {
let re = make_regexp(&to_value(Object::default()), &[args[0].clone()], 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.
/// <https://tc39.es/ecma262/#sec-stringpad/>
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.
/// Filler defaults to single space.
/// <https://tc39.es/ecma262/#sec-string.prototype.padend/>
/// The padding is applied from the end of the current string.
///
/// 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 {
let primitive_val: String = ctx.value_to_rust_string(this);
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.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.
/// Filler defaults to single space.
/// <https://tc39.es/ecma262/#sec-string.prototype.padstart/>
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.padstart
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart
pub fn pad_start(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let primitive_val: String = ctx.value_to_rust_string(this);
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)
}
/// Helper function to check if a `char` is trimmable.
fn is_trimmable_whitespace(c: char) -> bool {
// 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 {
let this_str: String = ctx.value_to_rust_string(this);
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 {
let this_str: String = ctx.value_to_rust_string(this);
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 {
let this_str: String = ctx.value_to_rust_string(this);
Ok(to_value(this_str.trim_end_matches(is_trimmable_whitespace)))
}
/// Return a String with every code point mapped to its corresponding lowercase equivalent.
/// With the current implementation the string is always copied even if the resulting String is identical
/// <https://tc39.es/ecma262/#sec-string.prototype.tolowercase>
/// `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 {
// 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
@ -680,9 +848,18 @@ pub fn to_lowercase(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultV
Ok(to_value(this_str.to_lowercase()))
}
/// Return a String with every code point mapped to its corresponding uppercase equivalent.
/// With the current implementation the string is always copied even if the resulting String is identical
/// <https://tc39.es/ecma262/#sec-string.prototype.touppercase>
/// `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 {
// 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
@ -692,13 +869,16 @@ pub fn to_uppercase(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultV
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.
/// 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
/// 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.
/// When no argument is specified, the returned String is the same as the original
/// <https://tc39.es/ecma262/#sec-string.prototype.substring>
/// `String.prototype.substring( indexStart[, indexEnd] )`
///
/// The `substring()` method returns the part of the `string` between the start and end indexes, or to the end of the string.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [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 {
// 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
@ -739,11 +919,16 @@ pub fn substring(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultV
Ok(to_value(extracted_string))
}
/// Return a String which is a subset of the String value resulting from converting this object to a String.
/// 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.
/// 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
/// `String.prototype.substr( start[, length] )`
///
/// The `substr()` method returns a portion of the string, starting at the specified index and extending for a given number of characters afterward.
///
/// 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>
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.
@ -792,16 +977,34 @@ pub fn substr(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValu
}
}
/// Get the string value to a primitive string
/// <https://tc39.es/ecma262/#sec-string.prototype.valueof>
/// 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 {
// Use the to_string method because it is specified to do the same thing in this case
to_string(this, args, ctx)
}
/// TODO: update this method to return iterator
/// Returns an array* of all results matching a string against a regular expression, including capturing groups
/// <https://tc39.es/ecma262/#sec-string.prototype.matchall>
/// `String.prototype.matchAll( regexp )`
///
/// 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 {
let re: Value = match args.get(0) {
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)]
mod tests;
@ -14,12 +31,16 @@ use crate::{
use gc::{Gc, GcCell};
use rand::random;
/// https://tc39.es/ecma262/#sec-symbol-description
/// Creates Symbol instances.
///
/// Symbol instances are ordinary objects that inherit properties from the Symbol prototype object.
/// Symbol instances have a [[SymbolData]] internal slot.
/// The [[SymbolData]] internal slot is the Symbol value represented by this Symbol object.
/// Symbol instances have a `[[SymbolData]]` internal slot.
/// 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 {
// 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.
@ -48,14 +69,32 @@ pub fn call_symbol(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultVa
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 {
let s: Value = this.get_internal_slot("Description");
let full_string = format!(r#"Symbol({})"#, s.to_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 {
// Create Symbol constructor (or function in Symbol's case)
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)]
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`)
#[must_use]
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 fn undefined() -> Value {
@ -80,8 +85,13 @@ impl ValueData {
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
/// For scalar types it should be false, for objects check the private field for extensibilaty. By default true
/// This will tell us if we can exten an object or not, not properly implemented yet
///
/// 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/freeze would also turn extensible to false/>
pub fn is_extensible(&self) -> bool {
@ -167,6 +177,7 @@ impl ValueData {
}
/// Returns true if the value is true
///
/// [toBoolean](https://tc39.es/ecma262/#sec-toboolean)
pub fn is_true(&self) -> bool {
match *self {
@ -217,6 +228,7 @@ impl ValueData {
}
/// 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
pub fn remove_prop(&self, field: &str) {
match *self {
@ -230,8 +242,9 @@ impl ValueData {
};
}
/// Resolve the property in the object
/// Returns a copy of the Property
/// Resolve the property in the object.
///
/// A copy of the Property is returned.
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
// 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
/// Returns a copy of the Property
/// Resolve the property in the object.
///
/// Returns a copy of the Property.
pub fn get_internal_slot(&self, field: &str) -> Value {
let obj: Object = match *self {
ValueData::Object(ref obj) => {
@ -578,6 +592,7 @@ impl ValueData {
}
}
/// Conversts the `Value` to `JSON`.
pub fn to_json(&self) -> JSONValue {
match *self {
ValueData::Null
@ -603,6 +618,7 @@ impl ValueData {
}
/// Get the type of the value
///
/// https://tc39.es/ecma262/#sec-typeof-operator
pub fn get_type(&self) -> &'static str {
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 environment_record_trait;
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)]
mod tests;

5
boa/src/lib.rs

@ -12,7 +12,7 @@
unused_lifetimes,
unreachable_pub,
trivial_numeric_casts,
rustdoc,
// rustdoc,
missing_debug_implementations,
missing_copy_implementations,
deprecated_in_future,
@ -29,7 +29,8 @@
clippy::cognitive_complexity,
clippy::must_use_candidate,
clippy::missing_errors_doc,
clippy::as_conversions
clippy::as_conversions,
missing_doc_code_examples
)]
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 std::fmt::{Display, Formatter, Result};
#[cfg(feature = "serde-ast")]
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))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
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),
// 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),
// 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),
// 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),
// 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,
// 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,
}

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::{
error,
fmt::{Display, Error, Formatter},
@ -7,83 +16,420 @@ use std::{
#[cfg(feature = "serde-ast")]
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))]
#[derive(Clone, Copy, PartialEq, Debug)]
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,
/// 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,
/// 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,
/// 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,
/// 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,
/// 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,
/// 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,
/// 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,
/// 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,
/// 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,
/// 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,
/// 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,
/// The `enum` keyword
/// The `enum` keyword.
///
/// Future reserved keyword.
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,
/// 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,
/// 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,
/// 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,
/// 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,
/// 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,
/// 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,
/// 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,
/// 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,
/// 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,
/// 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,
/// 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,
/// 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,
/// 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,
/// 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,
/// 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,
/// 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,
/// 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,
/// 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,
/// 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,
/// 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,
/// 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,
/// 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,
}

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

@ -1,3 +1,5 @@
//! The Javascript Abstract Syntax Tree.
pub mod constant;
pub mod keyword;
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::{
constant::Const,
op::{BinOp, Operator, UnaryOp},
@ -12,85 +14,482 @@ use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
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>),
/// 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>),
/// 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>),
/// 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>),
/// 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>),
/// 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>),
/// 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>),
/// 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>),
/// 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 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)>),
/// 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>),
/// 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>),
/// 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>),
/// 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),
/// 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>),
/// [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(
Option<Box<Node>>,
Option<Box<Node>>,
Option<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>>),
/// 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>)>),
/// 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),
/// 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>),
/// 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>),
/// 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>>),
/// 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>>),
/// `...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>),
// 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>),
/// 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>),
/// 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>),
/// 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(
Box<Node>,
Option<Box<Node>>,
Option<Box<Node>>,
Option<Box<Node>>,
),
/// The JavaScript `this` keyword refers to the object it belongs to.
///
/// 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
/// 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,
/// 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>),
/// 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>)>),
/// 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>),
}
@ -101,6 +500,7 @@ impl Operator for Node {
_ => true,
}
}
fn get_precedence(&self) -> u64 {
match self {
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,
/// not any value like numbers, strings, or objects.
///```javascript
///```text
///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))]
#[derive(Clone, Debug, PartialEq, Trace, Finalize)]
pub struct FormalParameter {
@ -348,6 +754,13 @@ pub struct FormalParameter {
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>;
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
#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Trace, Finalize)]
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),
/// 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),
/// 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),
/// 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),
}
/// 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))]
#[derive(Clone, Debug, PartialEq, Trace, Finalize)]
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,
/// 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,
/// 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,
// 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 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))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
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,
/// `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,
/// `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,
/// `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,
/// `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,
/// `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,
}
@ -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))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
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,
/// `++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,
/// `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,
/// `--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,
/// `-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,
/// `+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,
/// `!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,
/// `~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,
/// `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,
/// 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
/// 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.
@ -86,11 +287,18 @@ pub enum UnaryOp {
/// [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.
///
/// 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,
/// 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
/// expression that evaluates to `undefined` is desired.
/// 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),
/// `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,
}
@ -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))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
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,
/// `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,
/// `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,
/// `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,
/// `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,
/// `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,
}
@ -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))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
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,
/// `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,
/// `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,
/// `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,
/// `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,
/// `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,
/// `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,
/// `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,
}
@ -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))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
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,
/// `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,
}
@ -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))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub enum BinOp {
/// Numeric operation
/// Numeric operation.
///
/// see: [`NumOp`](enum.NumOp.html)
Num(NumOp),
/// Bitwise operation
/// Bitwise operation.
///
/// see: [`BitOp`](enum.BitOp.html).
Bit(BitOp),
/// Comparitive operation
/// Comparitive operation.
///
/// see: [`CompOp`](enum.CompOp.html).
Comp(CompOp),
/// Logical operation
/// Logical operation.
///
/// see: [`LogOp`](enum.LogOp.html).
Log(LogOp),
/// Assign operation
/// Assign operation.
///
/// see: [`AssignOp`](enum.AssignOp.html).
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))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
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,
/// `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,
/// `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,
/// `a **= b` - Exponent assign
Exp,
/// `a /= b` - Div assign
/// The division assignment operator divides 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#Division_assignment
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,
/// `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,
/// `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,
/// `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,
/// `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,
/// `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,
// TODO: Add UShl (unsigned shift left).
}
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")]
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 std::fmt::{Display, Error, Formatter};
#[cfg(feature = "serde-ast")]
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))]
#[derive(PartialEq, Clone, Copy, Debug)]
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 std::fmt::{Debug, Display, Formatter, Result};
#[cfg(feature = "serde-ast")]
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))]
#[derive(Debug, Clone, PartialEq)]
pub struct Token {
/// The token Data
/// The token kind, which contains the actual data of the token.
pub kind: TokenKind,
/// Token position from original source code
/// The token position from origina source code.
pub pos: Position,
}
@ -30,6 +43,7 @@ impl Display for Token {
}
}
/// A continuous sequence of tokens.
pub struct VecToken(Vec<Token>);
impl Debug for VecToken {
@ -46,25 +60,38 @@ impl Debug for VecToken {
#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))]
#[derive(Clone, PartialEq, Debug)]
pub enum TokenKind {
/// A boolean literal, which is either `true` or `false`
/// A boolean literal, which is either `true` or `false`.
BooleanLiteral(bool),
/// The end of the file
/// The end of the file.
EOF,
/// An identifier
/// An identifier.
Identifier(String),
/// A keyword
/// A keyword.
///
/// see: [`Keyword`](../keyword/enum.Keyword.html)
Keyword(Keyword),
/// A `null` literal
/// A `null` literal.
NullLiteral,
/// A numeric literal
/// A numeric literal.
NumericLiteral(f64),
/// A piece of punctuation
///
/// see: [`Punctuator`](../punc/enum.Punctuator.html)
Punctuator(Punctuator),
/// A string literal
/// A string literal.
StringLiteral(String),
/// A regular expression, consisting of body and flags
/// A regular expression, consisting of body and flags.
RegularExpressionLiteral(String, String),
/// Indicates the end of a line \n
/// Indicates the end of a line (`\n`).
LineTerminator,
}

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

@ -17,6 +17,8 @@ use std::{
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 {
($this:ident, $assign_op:expr, $op:expr) => ({
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 {
($this:ident, $assign_op:expr, $op:expr) => ({
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.
///
/// [LexerError] implements [fmt::Display] so you just display this value as an error
#[derive(Debug, Clone)]
pub struct LexerError {
/// details will be displayed when a LexerError occurs
details: String,
}
impl LexerError {
/// Create a new LexerError struct
///
/// * `msg` - The message to show when LexerError is displayed
fn new(msg: &str) -> Self {
Self {
details: msg.to_string(),
@ -107,13 +116,15 @@ impl error::Error for LexerError {
/// A lexical analyzer for JavaScript source code
#[derive(Debug)]
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>,
// The current line number in the script
/// The current line number in the script
line_number: u64,
// the current column number in the script
/// the current column number in the script
column_number: u64,
// The full string
/// The full Peekable buffer, an array of [Char]s
buffer: Peekable<Chars<'a>>,
}
@ -124,13 +135,6 @@ impl<'a> Lexer<'a> {
///
/// * `buffer` - A string slice that holds the source code.
/// 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> {
Lexer {
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) {
self.tokens
.push(Token::new(tk, self.line_number, self.column_number))
@ -204,6 +208,7 @@ impl<'a> Lexer<'a> {
result
}
/// Utility function for reading integers in different bases
fn read_integer_in_base(&mut self, base: u32, mut buf: String) -> Result<u64, LexerError> {
self.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"))
}
/// 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> {
match self.preview_next() {
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> {
loop {
// 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;
/// Lexical analysis (tokenizing/lexing).
pub mod lexer;
// Parses a sequence of tokens into expressions
pub mod parser;

Loading…
Cancel
Save