Browse Source

Custom JavaScript string rust type (#1380)

- Moved src/value/rcstring.rs => src/string.rs
pull/1411/head
Halid Odat 3 years ago committed by GitHub
parent
commit
be4a8724b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      boa/src/builtins/console/mod.rs
  2. 3
      boa/src/builtins/json/mod.rs
  3. 10
      boa/src/builtins/object/for_in_iterator.rs
  4. 14
      boa/src/builtins/regexp/mod.rs
  5. 9
      boa/src/builtins/regexp/regexp_string_iterator.rs
  6. 28
      boa/src/builtins/string/mod.rs
  7. 10
      boa/src/bytecompiler.rs
  8. 3
      boa/src/lib.rs
  9. 14
      boa/src/object/iter.rs
  10. 22
      boa/src/object/mod.rs
  11. 15
      boa/src/property/mod.rs
  12. 448
      boa/src/string.rs
  13. 10
      boa/src/symbol.rs
  14. 4
      boa/src/value/conversions.rs
  15. 12
      boa/src/value/mod.rs
  16. 10
      boa/src/value/operations.rs
  17. 114
      boa/src/value/rcstring.rs
  18. 4
      boa/src/vm/code_block.rs

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

@ -20,8 +20,8 @@ use crate::{
builtins::BuiltIn, builtins::BuiltIn,
object::ObjectInitializer, object::ObjectInitializer,
property::Attribute, property::Attribute,
value::{display::display_obj, RcString, Value}, value::{display::display_obj, Value},
BoaProfiler, Context, Result, BoaProfiler, Context, JsString, Result,
}; };
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use std::time::SystemTime; use std::time::SystemTime;
@ -137,8 +137,8 @@ pub fn formatter(data: &[Value], context: &mut Context) -> Result<String> {
/// This is the internal console object state. /// This is the internal console object state.
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub(crate) struct Console { pub(crate) struct Console {
count_map: FxHashMap<RcString, u32>, count_map: FxHashMap<JsString, u32>,
timer_map: FxHashMap<RcString, u128>, timer_map: FxHashMap<JsString, u128>,
groups: Vec<String>, groups: Vec<String>,
} }

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

@ -15,12 +15,13 @@
use crate::{ use crate::{
builtins::BuiltIn, builtins::BuiltIn,
object::Object,
object::ObjectInitializer, object::ObjectInitializer,
property::{Attribute, DataDescriptor, PropertyKey}, property::{Attribute, DataDescriptor, PropertyKey},
symbol::WellKnownSymbols,
value::IntegerOrInfinity, value::IntegerOrInfinity,
BoaProfiler, Context, Result, Value, BoaProfiler, Context, Result, Value,
}; };
use crate::{object::Object, symbol::WellKnownSymbols};
use serde::Serialize; use serde::Serialize;
use serde_json::{self, ser::PrettyFormatter, Serializer, Value as JSONValue}; use serde_json::{self, ser::PrettyFormatter, Serializer, Value as JSONValue};

10
boa/src/builtins/object/for_in_iterator.rs

@ -1,12 +1,12 @@
use crate::value::RcString;
use crate::{ use crate::{
builtins::{function::make_builtin_fn, iterable::create_iter_result_object}, builtins::{function::make_builtin_fn, iterable::create_iter_result_object},
gc::{Finalize, Trace}, gc::{Finalize, Trace},
object::{GcObject, ObjectData}, object::{GcObject, ObjectData},
property::PropertyKey,
property::{Attribute, DataDescriptor}, property::{Attribute, DataDescriptor},
BoaProfiler, Context, Result, Value, symbol::WellKnownSymbols,
BoaProfiler, Context, JsString, Result, Value,
}; };
use crate::{property::PropertyKey, symbol::WellKnownSymbols};
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use std::collections::VecDeque; use std::collections::VecDeque;
@ -20,8 +20,8 @@ use std::collections::VecDeque;
#[derive(Debug, Clone, Finalize, Trace)] #[derive(Debug, Clone, Finalize, Trace)]
pub struct ForInIterator { pub struct ForInIterator {
object: Value, object: Value,
visited_keys: FxHashSet<RcString>, visited_keys: FxHashSet<JsString>,
remaining_keys: VecDeque<RcString>, remaining_keys: VecDeque<JsString>,
object_was_visited: bool, object_was_visited: bool,
} }

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

@ -17,8 +17,8 @@ use crate::{
object::{ConstructorBuilder, FunctionBuilder, GcObject, ObjectData, PROTOTYPE}, object::{ConstructorBuilder, FunctionBuilder, GcObject, ObjectData, PROTOTYPE},
property::{Attribute, DataDescriptor}, property::{Attribute, DataDescriptor},
symbol::WellKnownSymbols, symbol::WellKnownSymbols,
value::{IntegerOrInfinity, RcString, Value}, value::{IntegerOrInfinity, Value},
BoaProfiler, Context, Result, BoaProfiler, Context, JsString, Result,
}; };
use regexp_string_iterator::RegExpStringIterator; use regexp_string_iterator::RegExpStringIterator;
use regress::Regex; use regress::Regex;
@ -650,7 +650,7 @@ impl RegExp {
/// [spec]: https://tc39.es/ecma262/#sec-regexpexec /// [spec]: https://tc39.es/ecma262/#sec-regexpexec
pub(crate) fn abstract_exec( pub(crate) fn abstract_exec(
this: &Value, this: &Value,
input: RcString, input: JsString,
context: &mut Context, context: &mut Context,
) -> Result<Value> { ) -> Result<Value> {
// 1. Assert: Type(R) is Object. // 1. Assert: Type(R) is Object.
@ -694,7 +694,7 @@ impl RegExp {
/// [spec]: https://tc39.es/ecma262/#sec-regexpbuiltinexec /// [spec]: https://tc39.es/ecma262/#sec-regexpbuiltinexec
pub(crate) fn abstract_builtin_exec( pub(crate) fn abstract_builtin_exec(
this: &Value, this: &Value,
input: RcString, input: JsString,
context: &mut Context, context: &mut Context,
) -> Result<Value> { ) -> Result<Value> {
// 1. Assert: R is an initialized RegExp instance. // 1. Assert: R is an initialized RegExp instance.
@ -1160,7 +1160,7 @@ impl RegExp {
} }
// 12. Let accumulatedResult be the empty String. // 12. Let accumulatedResult be the empty String.
let mut accumulated_result = RcString::from(""); let mut accumulated_result = JsString::new("");
// 13. Let nextSourcePosition be 0. // 13. Let nextSourcePosition be 0.
let mut next_source_position = 0; let mut next_source_position = 0;
@ -1227,7 +1227,7 @@ impl RegExp {
// k. If functionalReplace is true, then // k. If functionalReplace is true, then
// l. Else, // l. Else,
let replacement: RcString; let replacement: JsString;
if functional_replace { if functional_replace {
// i. Let replacerArgs be « matched ». // i. Let replacerArgs be « matched ».
let mut replacer_args = vec![Value::from(matched)]; let mut replacer_args = vec![Value::from(matched)];
@ -1553,7 +1553,7 @@ impl RegExp {
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-advancestringindex /// [spec]: https://tc39.es/ecma262/#sec-advancestringindex
fn advance_string_index(s: RcString, index: usize, unicode: bool) -> usize { fn advance_string_index(s: JsString, index: usize, unicode: bool) -> usize {
// Regress only works with utf8, so this function differs from the spec. // Regress only works with utf8, so this function differs from the spec.
// 1. Assert: index ≤ 2^53 - 1. // 1. Assert: index ≤ 2^53 - 1.

9
boa/src/builtins/regexp/regexp_string_iterator.rs

@ -17,15 +17,14 @@ use crate::{
object::{GcObject, ObjectData}, object::{GcObject, ObjectData},
property::{Attribute, DataDescriptor}, property::{Attribute, DataDescriptor},
symbol::WellKnownSymbols, symbol::WellKnownSymbols,
value::RcString, BoaProfiler, Context, JsString, Result, Value,
BoaProfiler, Context, Result, Value,
}; };
// TODO: See todos in create_regexp_string_iterator and next. // TODO: See todos in create_regexp_string_iterator and next.
#[derive(Debug, Clone, Finalize, Trace)] #[derive(Debug, Clone, Finalize, Trace)]
pub struct RegExpStringIterator { pub struct RegExpStringIterator {
matcher: Value, matcher: Value,
string: RcString, string: JsString,
global: bool, global: bool,
unicode: bool, unicode: bool,
completed: bool, completed: bool,
@ -33,7 +32,7 @@ pub struct RegExpStringIterator {
// TODO: See todos in create_regexp_string_iterator and next. // TODO: See todos in create_regexp_string_iterator and next.
impl RegExpStringIterator { impl RegExpStringIterator {
fn new(matcher: Value, string: RcString, global: bool, unicode: bool) -> Self { fn new(matcher: Value, string: JsString, global: bool, unicode: bool) -> Self {
Self { Self {
matcher, matcher,
string, string,
@ -51,7 +50,7 @@ impl RegExpStringIterator {
/// [spec]: https://tc39.es/ecma262/#sec-createregexpstringiterator /// [spec]: https://tc39.es/ecma262/#sec-createregexpstringiterator
pub(crate) fn create_regexp_string_iterator( pub(crate) fn create_regexp_string_iterator(
matcher: &Value, matcher: &Value,
string: RcString, string: JsString,
global: bool, global: bool,
unicode: bool, unicode: bool,
context: &mut Context, context: &mut Context,

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

@ -21,8 +21,7 @@ use crate::{
object::{ConstructorBuilder, Object, ObjectData}, object::{ConstructorBuilder, Object, ObjectData},
property::Attribute, property::Attribute,
symbol::WellKnownSymbols, symbol::WellKnownSymbols,
value::{RcString, Value}, BoaProfiler, Context, JsString, Result, Value,
BoaProfiler, Context, Result,
}; };
use std::{ use std::{
char::{decode_utf16, from_u32}, char::{decode_utf16, from_u32},
@ -31,7 +30,7 @@ use std::{
}; };
use unicode_normalization::UnicodeNormalization; use unicode_normalization::UnicodeNormalization;
pub(crate) fn code_point_at(string: RcString, position: i32) -> Option<(u32, u8, bool)> { pub(crate) fn code_point_at(string: JsString, position: i32) -> Option<(u32, u8, bool)> {
let size = string.encode_utf16().count() as i32; let size = string.encode_utf16().count() as i32;
if position < 0 || position >= size { if position < 0 || position >= size {
return None; return None;
@ -170,7 +169,7 @@ impl String {
.clone() .clone()
} }
Some(ref value) => value.to_string(context)?, Some(ref value) => value.to_string(context)?,
None => RcString::default(), None => JsString::default(),
}; };
if new_target.is_undefined() { if new_target.is_undefined() {
@ -202,7 +201,7 @@ impl String {
Ok(this) Ok(this)
} }
fn this_string_value(this: &Value, context: &mut Context) -> Result<RcString> { fn this_string_value(this: &Value, context: &mut Context) -> Result<JsString> {
match this { match this {
Value::String(ref string) => return Ok(string.clone()), Value::String(ref string) => return Ok(string.clone()),
Value::Object(ref object) => { Value::Object(ref object) => {
@ -685,10 +684,9 @@ impl String {
// 11. If functionalReplace is true, then // 11. If functionalReplace is true, then
// 12. Else, // 12. Else,
let replacement: RcString; let replacement = if functional_replace {
if functional_replace {
// a. Let replacement be ? ToString(? Call(replaceValue, undefined, « searchString, 𝔽(position), string »)). // a. Let replacement be ? ToString(? Call(replaceValue, undefined, « searchString, 𝔽(position), string »)).
replacement = context context
.call( .call(
&replace_value, &replace_value,
&Value::Undefined, &Value::Undefined,
@ -698,22 +696,22 @@ impl String {
this_str.clone().into(), this_str.clone().into(),
], ],
)? )?
.to_string(context)?; .to_string(context)?
} else { } else {
// a. Assert: Type(replaceValue) is String. // a. Assert: Type(replaceValue) is String.
// b. Let captures be a new empty List. // b. Let captures be a new empty List.
let captures = Vec::new(); let captures = Vec::new();
// c. Let replacement be ! GetSubstitution(searchString, string, position, captures, undefined, replaceValue). // c. Let replacement be ! GetSubstitution(searchString, string, position, captures, undefined, replaceValue).
replacement = get_substitution( get_substitution(
search_str.to_string(), search_str.to_string(),
this_str.to_string(), this_str.to_string(),
position.unwrap(), position.unwrap(),
captures, captures,
Value::undefined(), Value::undefined(),
replace_value.to_string(context)?.to_string(), replace_value.to_string(context)?.to_string(),
)?; )?
} };
// 13. Return the string-concatenation of preserved, replacement, and the substring of string from position + searchLength. // 13. Return the string-concatenation of preserved, replacement, and the substring of string from position + searchLength.
Ok(format!( Ok(format!(
@ -862,9 +860,9 @@ impl String {
/// Performs the actual string padding for padStart/End. /// Performs the actual string padding for padStart/End.
/// <https://tc39.es/ecma262/#sec-stringpad/> /// <https://tc39.es/ecma262/#sec-stringpad/>
fn string_pad( fn string_pad(
primitive: RcString, primitive: JsString,
max_length: i32, max_length: i32,
fill_string: Option<RcString>, fill_string: Option<JsString>,
at_start: bool, at_start: bool,
) -> Value { ) -> Value {
let primitive_length = primitive.len() as i32; let primitive_length = primitive.len() as i32;
@ -1480,7 +1478,7 @@ pub(crate) fn get_substitution(
captures: Vec<Value>, captures: Vec<Value>,
_named_captures: Value, _named_captures: Value,
replacement: StdString, replacement: StdString,
) -> Result<RcString> { ) -> Result<JsString> {
// 1. Assert: Type(matched) is String. // 1. Assert: Type(matched) is String.
// 2. Let matchLength be the number of code units in matched. // 2. Let matchLength be the number of code units in matched.

10
boa/src/bytecompiler.rs

@ -4,9 +4,9 @@ use crate::{
op::{AssignOp, BinOp, BitOp, CompOp, LogOp, NumOp, UnaryOp}, op::{AssignOp, BinOp, BitOp, CompOp, LogOp, NumOp, UnaryOp},
Const, Node, Const, Node,
}, },
value::{RcBigInt, RcString}, value::RcBigInt,
vm::{CodeBlock, Opcode}, vm::{CodeBlock, Opcode},
Value, JsString, Value,
}; };
use std::collections::HashMap; use std::collections::HashMap;
@ -34,7 +34,7 @@ fn u64_to_array(value: u64) -> [u8; 8] {
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum Literal { enum Literal {
String(RcString), String(JsString),
BigInt(RcBigInt), BigInt(RcBigInt),
} }
@ -64,7 +64,7 @@ enum Access<'a> {
pub struct ByteCompiler { pub struct ByteCompiler {
code_block: CodeBlock, code_block: CodeBlock,
literals_map: HashMap<Literal, u32>, literals_map: HashMap<Literal, u32>,
names_map: HashMap<RcString, u32>, names_map: HashMap<JsString, u32>,
loops: Vec<LoopControlInfo>, loops: Vec<LoopControlInfo>,
} }
@ -111,7 +111,7 @@ impl ByteCompiler {
return *index; return *index;
} }
let name: RcString = name.into(); let name = JsString::new(name);
let index = self.code_block.names.len() as u32; let index = self.code_block.names.len() as u32;
self.code_block.names.push(name.clone()); self.code_block.names.push(name.clone());
self.names_map.insert(name, index); self.names_map.insert(name, index);

3
boa/src/lib.rs

@ -54,6 +54,7 @@ pub mod object;
pub mod profiler; pub mod profiler;
pub mod property; pub mod property;
pub mod realm; pub mod realm;
pub mod string;
pub mod symbol; pub mod symbol;
// syntax module has a lot of acronyms // syntax module has a lot of acronyms
#[allow(clippy::upper_case_acronyms)] #[allow(clippy::upper_case_acronyms)]
@ -71,7 +72,7 @@ pub(crate) use crate::{exec::Executable, profiler::BoaProfiler};
// Export things to root level // Export things to root level
#[doc(inline)] #[doc(inline)]
pub use crate::{context::Context, symbol::JsSymbol, value::Value}; pub use crate::{context::Context, string::JsString, symbol::JsSymbol, value::Value};
use crate::syntax::{ use crate::syntax::{
ast::node::StatementList, ast::node::StatementList,

14
boa/src/object/iter.rs

@ -1,5 +1,5 @@
use super::{Object, PropertyDescriptor, PropertyKey}; use super::{Object, PropertyDescriptor, PropertyKey};
use crate::{value::RcString, JsSymbol}; use crate::{JsString, JsSymbol};
use std::{collections::hash_map, iter::FusedIterator}; use std::{collections::hash_map, iter::FusedIterator};
impl Object { impl Object {
@ -109,7 +109,7 @@ impl Object {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Iter<'a> { pub struct Iter<'a> {
indexed_properties: hash_map::Iter<'a, u32, PropertyDescriptor>, indexed_properties: hash_map::Iter<'a, u32, PropertyDescriptor>,
string_properties: hash_map::Iter<'a, RcString, PropertyDescriptor>, string_properties: hash_map::Iter<'a, JsString, PropertyDescriptor>,
symbol_properties: hash_map::Iter<'a, JsSymbol, PropertyDescriptor>, symbol_properties: hash_map::Iter<'a, JsSymbol, PropertyDescriptor>,
} }
@ -342,10 +342,10 @@ impl FusedIterator for IndexPropertyValues<'_> {}
/// An iterator over the `String` property entries of an `Object` /// An iterator over the `String` property entries of an `Object`
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct StringProperties<'a>(hash_map::Iter<'a, RcString, PropertyDescriptor>); pub struct StringProperties<'a>(hash_map::Iter<'a, JsString, PropertyDescriptor>);
impl<'a> Iterator for StringProperties<'a> { impl<'a> Iterator for StringProperties<'a> {
type Item = (&'a RcString, &'a PropertyDescriptor); type Item = (&'a JsString, &'a PropertyDescriptor);
#[inline] #[inline]
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
@ -369,10 +369,10 @@ impl FusedIterator for StringProperties<'_> {}
/// An iterator over the string keys (`RcString`) of an `Object`. /// An iterator over the string keys (`RcString`) of an `Object`.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct StringPropertyKeys<'a>(hash_map::Keys<'a, RcString, PropertyDescriptor>); pub struct StringPropertyKeys<'a>(hash_map::Keys<'a, JsString, PropertyDescriptor>);
impl<'a> Iterator for StringPropertyKeys<'a> { impl<'a> Iterator for StringPropertyKeys<'a> {
type Item = &'a RcString; type Item = &'a JsString;
#[inline] #[inline]
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
@ -396,7 +396,7 @@ impl FusedIterator for StringPropertyKeys<'_> {}
/// An iterator over the string values (`Property`) of an `Object`. /// An iterator over the string values (`Property`) of an `Object`.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct StringPropertyValues<'a>(hash_map::Values<'a, RcString, PropertyDescriptor>); pub struct StringPropertyValues<'a>(hash_map::Values<'a, JsString, PropertyDescriptor>);
impl<'a> Iterator for StringPropertyValues<'a> { impl<'a> Iterator for StringPropertyValues<'a> {
type Item = &'a PropertyDescriptor; type Item = &'a PropertyDescriptor;

22
boa/src/object/mod.rs

@ -15,8 +15,8 @@ use crate::{
context::StandardConstructor, context::StandardConstructor,
gc::{Finalize, Trace}, gc::{Finalize, Trace},
property::{AccessorDescriptor, Attribute, DataDescriptor, PropertyDescriptor, PropertyKey}, property::{AccessorDescriptor, Attribute, DataDescriptor, PropertyDescriptor, PropertyKey},
value::{RcBigInt, RcString, Value}, value::{RcBigInt, Value},
BoaProfiler, Context, JsSymbol, BoaProfiler, Context, JsString, JsSymbol,
}; };
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use std::{ use std::{
@ -69,7 +69,7 @@ pub struct Object {
pub data: ObjectData, pub data: ObjectData,
indexed_properties: FxHashMap<u32, PropertyDescriptor>, indexed_properties: FxHashMap<u32, PropertyDescriptor>,
/// Properties /// Properties
string_properties: FxHashMap<RcString, PropertyDescriptor>, string_properties: FxHashMap<JsString, PropertyDescriptor>,
/// Symbol Properties /// Symbol Properties
symbol_properties: FxHashMap<JsSymbol, PropertyDescriptor>, symbol_properties: FxHashMap<JsSymbol, PropertyDescriptor>,
/// Instance prototype `__proto__`. /// Instance prototype `__proto__`.
@ -93,7 +93,7 @@ pub enum ObjectData {
Function(Function), Function(Function),
Set(OrderedSet<Value>), Set(OrderedSet<Value>),
SetIterator(SetIterator), SetIterator(SetIterator),
String(RcString), String(JsString),
StringIterator(StringIterator), StringIterator(StringIterator),
Number(f64), Number(f64),
Symbol(JsSymbol), Symbol(JsSymbol),
@ -216,7 +216,7 @@ impl Object {
#[inline] #[inline]
pub fn string<S>(value: S) -> Self pub fn string<S>(value: S) -> Self
where where
S: Into<RcString>, S: Into<JsString>,
{ {
Self { Self {
data: ObjectData::String(value.into()), data: ObjectData::String(value.into()),
@ -413,7 +413,7 @@ impl Object {
} }
#[inline] #[inline]
pub fn as_string(&self) -> Option<RcString> { pub fn as_string(&self) -> Option<JsString> {
match self.data { match self.data {
ObjectData::String(ref string) => Some(string.clone()), ObjectData::String(ref string) => Some(string.clone()),
_ => None, _ => None,
@ -628,13 +628,13 @@ impl Object {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct FunctionBinding { pub struct FunctionBinding {
binding: PropertyKey, binding: PropertyKey,
name: RcString, name: JsString,
} }
impl From<&str> for FunctionBinding { impl From<&str> for FunctionBinding {
#[inline] #[inline]
fn from(name: &str) -> Self { fn from(name: &str) -> Self {
let name: RcString = name.into(); let name: JsString = name.into();
Self { Self {
binding: name.clone().into(), binding: name.clone().into(),
@ -646,7 +646,7 @@ impl From<&str> for FunctionBinding {
impl From<String> for FunctionBinding { impl From<String> for FunctionBinding {
#[inline] #[inline]
fn from(name: String) -> Self { fn from(name: String) -> Self {
let name: RcString = name.into(); let name: JsString = name.into();
Self { Self {
binding: name.clone().into(), binding: name.clone().into(),
@ -655,9 +655,9 @@ impl From<String> for FunctionBinding {
} }
} }
impl From<RcString> for FunctionBinding { impl From<JsString> for FunctionBinding {
#[inline] #[inline]
fn from(name: RcString) -> Self { fn from(name: JsString) -> Self {
Self { Self {
binding: name.clone().into(), binding: name.clone().into(),
name, name,

15
boa/src/property/mod.rs

@ -17,8 +17,7 @@
use crate::{ use crate::{
gc::{Finalize, Trace}, gc::{Finalize, Trace},
object::GcObject, object::GcObject,
value::{RcString, Value}, JsString, JsSymbol, Value,
JsSymbol,
}; };
use std::{convert::TryFrom, fmt}; use std::{convert::TryFrom, fmt};
@ -306,14 +305,14 @@ impl PropertyDescriptor {
/// [spec]: https://tc39.es/ecma262/#sec-ispropertykey /// [spec]: https://tc39.es/ecma262/#sec-ispropertykey
#[derive(Trace, Finalize, Debug, Clone)] #[derive(Trace, Finalize, Debug, Clone)]
pub enum PropertyKey { pub enum PropertyKey {
String(RcString), String(JsString),
Symbol(JsSymbol), Symbol(JsSymbol),
Index(u32), Index(u32),
} }
impl From<RcString> for PropertyKey { impl From<JsString> for PropertyKey {
#[inline] #[inline]
fn from(string: RcString) -> PropertyKey { fn from(string: JsString) -> PropertyKey {
if let Ok(index) = string.parse() { if let Ok(index) = string.parse() {
PropertyKey::Index(index) PropertyKey::Index(index)
} else { } else {
@ -430,7 +429,7 @@ impl From<usize> for PropertyKey {
if let Ok(index) = u32::try_from(value) { if let Ok(index) = u32::try_from(value) {
PropertyKey::Index(index) PropertyKey::Index(index)
} else { } else {
PropertyKey::String(RcString::from(value.to_string())) PropertyKey::String(JsString::from(value.to_string()))
} }
} }
} }
@ -440,7 +439,7 @@ impl From<isize> for PropertyKey {
if let Ok(index) = u32::try_from(value) { if let Ok(index) = u32::try_from(value) {
PropertyKey::Index(index) PropertyKey::Index(index)
} else { } else {
PropertyKey::String(RcString::from(value.to_string())) PropertyKey::String(JsString::from(value.to_string()))
} }
} }
} }
@ -450,7 +449,7 @@ impl From<i32> for PropertyKey {
if let Ok(index) = u32::try_from(value) { if let Ok(index) = u32::try_from(value) {
PropertyKey::Index(index) PropertyKey::Index(index)
} else { } else {
PropertyKey::String(RcString::from(value.to_string())) PropertyKey::String(JsString::from(value.to_string()))
} }
} }
} }

448
boa/src/string.rs

@ -0,0 +1,448 @@
use crate::gc::{empty_trace, Finalize, Trace};
use std::{
alloc::{alloc, dealloc, Layout},
borrow::Borrow,
cell::Cell,
hash::{Hash, Hasher},
marker::PhantomData,
ops::Deref,
ptr::{copy_nonoverlapping, NonNull},
};
/// The inner representation of a [`JsString`].
#[repr(C)]
struct Inner {
/// The utf8 length, the number of bytes.
len: usize,
/// The number of references to the string.
///
/// When this reaches `0` the string is deallocated.
refcount: Cell<usize>,
/// An empty array which is used to get the offset of string data.
data: [u8; 0],
}
impl Inner {
/// Create a new `Inner` from `&str`.
#[inline]
fn new(s: &str) -> NonNull<Self> {
// We get the layout of the `Inner` type and we extend by the size
// of the string array.
let inner_layout = Layout::new::<Inner>();
let (layout, offset) = inner_layout
.extend(Layout::array::<u8>(s.len()).unwrap())
.unwrap();
let inner = unsafe {
let inner = alloc(layout) as *mut Inner;
// Write the first part, the Inner.
inner.write(Inner {
len: s.len(),
refcount: Cell::new(1),
data: [0; 0],
});
// Get offset into the string data.
let data = (*inner).data.as_mut_ptr();
debug_assert!(std::ptr::eq(inner.cast::<u8>().add(offset), data));
// Copy string data into data offset.
copy_nonoverlapping(s.as_ptr(), data, s.len());
inner
};
// Safety: We already know it's not null, so this is safe.
unsafe { NonNull::new_unchecked(inner) }
}
/// Concatinate two string.
#[inline]
fn concat(x: &str, y: &str) -> NonNull<Inner> {
let total_string_size = x.len() + y.len();
// We get the layout of the `Inner` type and we extend by the size
// of the string array.
let inner_layout = Layout::new::<Inner>();
let (layout, offset) = inner_layout
.extend(Layout::array::<u8>(total_string_size).unwrap())
.unwrap();
let inner = unsafe {
let inner = alloc(layout) as *mut Inner;
// Write the first part, the Inner.
inner.write(Inner {
len: total_string_size,
refcount: Cell::new(1),
data: [0; 0],
});
// Get offset into the string data.
let data = (*inner).data.as_mut_ptr();
debug_assert!(std::ptr::eq(inner.cast::<u8>().add(offset), data));
// Copy the two string data into data offset.
copy_nonoverlapping(x.as_ptr(), data, x.len());
copy_nonoverlapping(y.as_ptr(), data.add(x.len()), y.len());
inner
};
// Safety: We already know it's not null, so this is safe.
unsafe { NonNull::new_unchecked(inner) }
}
/// Deallocate inner type with string data.
#[inline]
unsafe fn dealloc(x: NonNull<Inner>) {
let len = (*x.as_ptr()).len;
let inner_layout = Layout::new::<Inner>();
let (layout, _offset) = inner_layout
.extend(Layout::array::<u8>(len).unwrap())
.unwrap();
dealloc(x.as_ptr() as _, layout);
}
}
/// This represents a JavaScript primitive string.
///
/// This is similar to `Rc<str>`. But unlike `Rc<str>` which stores the length
/// on the stack and a pointer to the data (this is also known as fat pointers).
/// The `JsString` length and data is stored on the heap. and just an non-null
/// pointer is kept, so its size is the size of a pointer.
pub struct JsString {
inner: NonNull<Inner>,
_marker: PhantomData<std::rc::Rc<str>>,
}
impl Default for JsString {
#[inline]
fn default() -> Self {
Self::new("")
}
}
impl JsString {
/// Create a new JavaScript string.
#[inline]
pub fn new<S: AsRef<str>>(s: S) -> Self {
let s = s.as_ref();
Self {
inner: Inner::new(s),
_marker: PhantomData,
}
}
/// Concatinate two string.
pub fn concat<T, U>(x: T, y: U) -> JsString
where
T: AsRef<str>,
U: AsRef<str>,
{
let x = x.as_ref();
let y = y.as_ref();
Self {
inner: Inner::concat(x, y),
_marker: PhantomData,
}
}
/// Return the inner representation.
#[inline]
fn inner(&self) -> &Inner {
unsafe { self.inner.as_ref() }
}
/// Return the JavaScript string as a rust `&str`.
#[inline]
pub fn as_str(&self) -> &str {
let inner = self.inner();
unsafe {
let slice = std::slice::from_raw_parts(inner.data.as_ptr(), inner.len);
std::str::from_utf8_unchecked(slice)
}
}
/// Gets the number of `JsString`s which point to this allocation.
#[inline]
pub fn refcount(this: &Self) -> usize {
this.inner().refcount.get()
}
/// Returns `true` if the two `JsString`s point to the same allocation (in a vein similar to [`ptr::eq`]).
///
/// [`ptr::eq`]: std::ptr::eq
#[inline]
pub fn ptr_eq(x: &Self, y: &Self) -> bool {
x.inner == y.inner
}
}
impl Finalize for JsString {}
// Safety: [`JsString`] does not contain any objects which recquire trace,
// so this is safe.
unsafe impl Trace for JsString {
empty_trace!();
}
impl Clone for JsString {
#[inline]
fn clone(&self) -> Self {
let inner = self.inner();
inner.refcount.set(inner.refcount.get() + 1);
JsString {
inner: self.inner,
_marker: PhantomData,
}
}
}
impl Drop for JsString {
#[inline]
fn drop(&mut self) {
let inner = self.inner();
if inner.refcount.get() == 1 {
// Safety: If refcount is 1 and we call drop, that means this is the last
// JsString which points to this memory allocation, so deallocating it is safe.
unsafe {
Inner::dealloc(self.inner);
}
} else {
inner.refcount.set(inner.refcount.get() - 1);
}
}
}
impl std::fmt::Debug for JsString {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.as_str().fmt(f)
}
}
impl std::fmt::Display for JsString {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.as_str().fmt(f)
}
}
impl From<&str> for JsString {
#[inline]
fn from(s: &str) -> Self {
Self::new(s)
}
}
impl From<Box<str>> for JsString {
#[inline]
fn from(s: Box<str>) -> Self {
Self::new(s)
}
}
impl From<String> for JsString {
#[inline]
fn from(s: String) -> Self {
Self::new(s)
}
}
impl AsRef<str> for JsString {
#[inline]
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl Borrow<str> for JsString {
#[inline]
fn borrow(&self) -> &str {
self.as_str()
}
}
impl Deref for JsString {
type Target = str;
#[inline]
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
impl PartialEq<JsString> for JsString {
#[inline]
fn eq(&self, other: &Self) -> bool {
// If they point at the same memory allocation, then they are equal.
if Self::ptr_eq(self, other) {
return true;
}
self.as_str() == other.as_str()
}
}
impl Eq for JsString {}
impl Hash for JsString {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_str().hash(state);
}
}
impl PartialOrd for JsString {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.as_str().partial_cmp(other)
}
}
impl Ord for JsString {
#[inline]
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.as_str().cmp(other)
}
}
impl PartialEq<str> for JsString {
#[inline]
fn eq(&self, other: &str) -> bool {
self.as_str() == other
}
}
impl PartialEq<JsString> for str {
#[inline]
fn eq(&self, other: &JsString) -> bool {
self == other.as_str()
}
}
impl PartialEq<&str> for JsString {
#[inline]
fn eq(&self, other: &&str) -> bool {
self.as_str() == *other
}
}
impl PartialEq<JsString> for &str {
#[inline]
fn eq(&self, other: &JsString) -> bool {
*self == other.as_str()
}
}
#[cfg(test)]
mod tests {
use super::JsString;
use std::mem::size_of;
#[test]
fn empty() {
let _ = JsString::new("");
}
#[test]
fn pointer_size() {
assert_eq!(size_of::<JsString>(), size_of::<*const u8>());
assert_eq!(size_of::<Option<JsString>>(), size_of::<*const u8>());
}
#[test]
fn refcount() {
let x = JsString::new("Hello wrold");
assert_eq!(JsString::refcount(&x), 1);
{
let y = x.clone();
assert_eq!(JsString::refcount(&x), 2);
assert_eq!(JsString::refcount(&y), 2);
{
let z = y.clone();
assert_eq!(JsString::refcount(&x), 3);
assert_eq!(JsString::refcount(&y), 3);
assert_eq!(JsString::refcount(&z), 3);
}
assert_eq!(JsString::refcount(&x), 2);
assert_eq!(JsString::refcount(&y), 2);
}
assert_eq!(JsString::refcount(&x), 1);
}
#[test]
fn ptr_eq() {
let x = JsString::new("Hello");
let y = x.clone();
assert!(JsString::ptr_eq(&x, &y));
let z = JsString::new("Hello");
assert!(!JsString::ptr_eq(&x, &z));
assert!(!JsString::ptr_eq(&y, &z));
}
#[test]
fn as_str() {
let s = "Hello";
let x = JsString::new(s);
assert_eq!(x.as_str(), s);
}
#[test]
fn hash() {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let s = "Hello, world!";
let x = JsString::new(s);
assert_eq!(x.as_str(), s);
let mut hasher = DefaultHasher::new();
s.hash(&mut hasher);
let s_hash = hasher.finish();
let mut hasher = DefaultHasher::new();
x.hash(&mut hasher);
let x_hash = hasher.finish();
assert_eq!(s_hash, x_hash);
}
#[test]
fn concat() {
let x = JsString::new("hello");
let y = ", ";
let z = JsString::new("world");
let w = String::from("!");
let xy = JsString::concat(x, y);
assert_eq!(xy, "hello, ");
assert_eq!(JsString::refcount(&xy), 1);
let xyz = JsString::concat(xy, z);
assert_eq!(xyz, "hello, world");
assert_eq!(JsString::refcount(&xyz), 1);
let xyzw = JsString::concat(xyz, w);
assert_eq!(xyzw, "hello, world!");
assert_eq!(JsString::refcount(&xyzw), 1);
}
}

10
boa/src/symbol.rs

@ -17,7 +17,7 @@
use crate::{ use crate::{
gc::{empty_trace, Finalize, Trace}, gc::{empty_trace, Finalize, Trace},
value::RcString, JsString,
}; };
use std::{ use std::{
cell::Cell, cell::Cell,
@ -246,7 +246,7 @@ impl WellKnownSymbols {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct Inner { struct Inner {
hash: u64, hash: u64,
description: Option<RcString>, description: Option<JsString>,
} }
/// This represents a JavaScript symbol primitive. /// This represents a JavaScript symbol primitive.
@ -258,7 +258,7 @@ pub struct JsSymbol {
impl JsSymbol { impl JsSymbol {
/// Create a new symbol. /// Create a new symbol.
#[inline] #[inline]
pub fn new(description: Option<RcString>) -> Self { pub fn new(description: Option<JsString>) -> Self {
let hash = SYMBOL_HASH_COUNT.with(|count| { let hash = SYMBOL_HASH_COUNT.with(|count| {
let hash = count.get(); let hash = count.get();
count.set(hash + 1); count.set(hash + 1);
@ -272,7 +272,7 @@ impl JsSymbol {
/// Create a new symbol with a specified hash and description. /// Create a new symbol with a specified hash and description.
#[inline] #[inline]
fn with_hash(hash: u64, description: Option<RcString>) -> Self { fn with_hash(hash: u64, description: Option<JsString>) -> Self {
Self { Self {
inner: Rc::new(Inner { hash, description }), inner: Rc::new(Inner { hash, description }),
} }
@ -280,7 +280,7 @@ impl JsSymbol {
/// Returns the `Symbol`s description. /// Returns the `Symbol`s description.
#[inline] #[inline]
pub fn description(&self) -> Option<RcString> { pub fn description(&self) -> Option<JsString> {
self.inner.description.clone() self.inner.description.clone()
} }

4
boa/src/value/conversions.rs

@ -44,9 +44,9 @@ impl From<char> for Value {
} }
} }
impl From<RcString> for Value { impl From<JsString> for Value {
#[inline] #[inline]
fn from(value: RcString) -> Self { fn from(value: JsString) -> Self {
Value::String(value) Value::String(value)
} }
} }

12
boa/src/value/mod.rs

@ -14,7 +14,7 @@ use crate::{
object::{GcObject, Object, ObjectData}, object::{GcObject, Object, ObjectData},
property::{Attribute, DataDescriptor, PropertyDescriptor, PropertyKey}, property::{Attribute, DataDescriptor, PropertyDescriptor, PropertyKey},
symbol::{JsSymbol, WellKnownSymbols}, symbol::{JsSymbol, WellKnownSymbols},
BoaProfiler, Context, Result, BoaProfiler, Context, JsString, Result,
}; };
use gc::{Finalize, Trace}; use gc::{Finalize, Trace};
use serde_json::{Number as JSONNumber, Value as JSONValue}; use serde_json::{Number as JSONNumber, Value as JSONValue};
@ -31,7 +31,6 @@ mod equality;
mod hash; mod hash;
mod operations; mod operations;
mod rcbigint; mod rcbigint;
mod rcstring;
mod r#type; mod r#type;
pub use conversions::*; pub use conversions::*;
@ -41,7 +40,6 @@ pub use hash::*;
pub use operations::*; pub use operations::*;
pub use r#type::Type; pub use r#type::Type;
pub use rcbigint::RcBigInt; pub use rcbigint::RcBigInt;
pub use rcstring::RcString;
/// A Javascript value /// A Javascript value
#[derive(Trace, Finalize, Debug, Clone)] #[derive(Trace, Finalize, Debug, Clone)]
@ -53,7 +51,7 @@ pub enum Value {
/// `boolean` - A `true` / `false` value, for if a certain criteria is met. /// `boolean` - A `true` / `false` value, for if a certain criteria is met.
Boolean(bool), Boolean(bool),
/// `String` - A UTF-8 string, such as `"Hello, world"`. /// `String` - A UTF-8 string, such as `"Hello, world"`.
String(RcString), String(JsString),
/// `Number` - A 64-bit floating point number, such as `3.1415` /// `Number` - A 64-bit floating point number, such as `3.1415`
Rational(f64), Rational(f64),
/// `Number` - A 32-bit integer, such as `42`. /// `Number` - A 32-bit integer, such as `42`.
@ -109,7 +107,7 @@ impl Value {
#[inline] #[inline]
pub fn string<S>(value: S) -> Self pub fn string<S>(value: S) -> Self
where where
S: Into<RcString>, S: Into<JsString>,
{ {
Self::String(value.into()) Self::String(value.into())
} }
@ -368,7 +366,7 @@ impl Value {
/// Returns the string if the values is a string, otherwise `None`. /// Returns the string if the values is a string, otherwise `None`.
#[inline] #[inline]
pub fn as_string(&self) -> Option<&RcString> { pub fn as_string(&self) -> Option<&JsString> {
match self { match self {
Self::String(ref string) => Some(string), Self::String(ref string) => Some(string),
_ => None, _ => None,
@ -643,7 +641,7 @@ impl Value {
/// Converts the value to a string. /// Converts the value to a string.
/// ///
/// This function is equivalent to `String(value)` in JavaScript. /// This function is equivalent to `String(value)` in JavaScript.
pub fn to_string(&self, context: &mut Context) -> Result<RcString> { pub fn to_string(&self, context: &mut Context) -> Result<JsString> {
match self { match self {
Value::Null => Ok("null".into()), Value::Null => Ok("null".into()),
Value::Undefined => Ok("undefined".into()), Value::Undefined => Ok("undefined".into()),

10
boa/src/value/operations.rs

@ -11,9 +11,9 @@ impl Value {
(Self::Integer(x), Self::Rational(y)) => Self::rational(f64::from(*x) + y), (Self::Integer(x), Self::Rational(y)) => Self::rational(f64::from(*x) + y),
(Self::Rational(x), Self::Integer(y)) => Self::rational(x + f64::from(*y)), (Self::Rational(x), Self::Integer(y)) => Self::rational(x + f64::from(*y)),
(Self::String(ref x), Self::String(ref y)) => Self::string(format!("{}{}", x, y)), (Self::String(ref x), Self::String(ref y)) => Self::from(JsString::concat(x, y)),
(Self::String(ref x), ref y) => Self::string(format!("{}{}", x, y.to_string(context)?)), (Self::String(ref x), ref y) => Self::from(JsString::concat(x, y.to_string(context)?)),
(ref x, Self::String(ref y)) => Self::string(format!("{}{}", x.to_string(context)?, y)), (ref x, Self::String(ref y)) => Self::from(JsString::concat(x.to_string(context)?, y)),
(Self::BigInt(ref n1), Self::BigInt(ref n2)) => { (Self::BigInt(ref n1), Self::BigInt(ref n2)) => {
Self::bigint(n1.as_inner().clone() + n2.as_inner().clone()) Self::bigint(n1.as_inner().clone() + n2.as_inner().clone())
} }
@ -24,10 +24,10 @@ impl Value {
other.to_primitive(context, PreferredType::Default)?, other.to_primitive(context, PreferredType::Default)?,
) { ) {
(Self::String(ref x), ref y) => { (Self::String(ref x), ref y) => {
Self::string(format!("{}{}", x, y.to_string(context)?)) Self::from(JsString::concat(x, y.to_string(context)?))
} }
(ref x, Self::String(ref y)) => { (ref x, Self::String(ref y)) => {
Self::string(format!("{}{}", x.to_string(context)?, y)) Self::from(JsString::concat(x.to_string(context)?, y))
} }
(x, y) => match (x.to_numeric(context)?, y.to_numeric(context)?) { (x, y) => match (x.to_numeric(context)?, y.to_numeric(context)?) {
(Numeric::Number(x), Numeric::Number(y)) => Self::rational(x + y), (Numeric::Number(x), Numeric::Number(y)) => Self::rational(x + y),

114
boa/src/value/rcstring.rs

@ -1,114 +0,0 @@
use crate::gc::{empty_trace, Finalize, Trace};
use std::{
borrow::Borrow,
fmt::{self, Display},
ops::Deref,
rc::Rc,
};
#[derive(Debug, Finalize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RcString(Rc<str>);
unsafe impl Trace for RcString {
empty_trace!();
}
impl RcString {
#[inline]
pub fn as_str(&self) -> &str {
&self.0
}
}
impl Default for RcString {
#[inline]
fn default() -> Self {
Self(Rc::from(String::new()))
}
}
impl Display for RcString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
impl PartialEq<str> for RcString {
#[inline]
fn eq(&self, other: &str) -> bool {
self.as_str() == other
}
}
impl PartialEq<RcString> for str {
#[inline]
fn eq(&self, other: &RcString) -> bool {
self == other.as_str()
}
}
impl PartialEq<&str> for RcString {
#[inline]
fn eq(&self, other: &&str) -> bool {
self.as_str() == *other
}
}
impl PartialEq<RcString> for &str {
#[inline]
fn eq(&self, other: &RcString) -> bool {
*self == other.as_str()
}
}
impl Deref for RcString {
type Target = str;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Borrow<str> for RcString {
#[inline]
fn borrow(&self) -> &str {
self.0.borrow()
}
}
impl AsRef<str> for RcString {
#[inline]
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl From<String> for RcString {
#[inline]
fn from(string: String) -> Self {
Self(Rc::from(string))
}
}
impl From<&RcString> for String {
#[inline]
fn from(string: &RcString) -> Self {
string.to_string()
}
}
impl From<Box<str>> for RcString {
#[inline]
fn from(string: Box<str>) -> Self {
Self(Rc::from(string))
}
}
impl From<&str> for RcString {
#[inline]
fn from(string: &str) -> Self {
Self(Rc::from(string))
}
}

4
boa/src/vm/code_block.rs

@ -1,4 +1,4 @@
use crate::{value::RcString, vm::Opcode, Value}; use crate::{vm::Opcode, JsString, Value};
use std::{convert::TryInto, fmt::Write, mem::size_of}; use std::{convert::TryInto, fmt::Write, mem::size_of};
@ -25,7 +25,7 @@ pub struct CodeBlock {
pub(crate) literals: Vec<Value>, pub(crate) literals: Vec<Value>,
/// Variables names /// Variables names
pub(crate) names: Vec<RcString>, pub(crate) names: Vec<JsString>,
} }
impl Default for CodeBlock { impl Default for CodeBlock {

Loading…
Cancel
Save