From e8ef21def1ff2f61f01e851873a2cd324165bd7d Mon Sep 17 00:00:00 2001 From: YXL Date: Sat, 23 Apr 2022 11:39:17 +0800 Subject: [PATCH 01/14] perf(string): No more heap allocation of static strings Signed-off-by: YXL --- boa_engine/src/string.rs | 376 ++++++++++++++++++++++----------------- 1 file changed, 215 insertions(+), 161 deletions(-) diff --git a/boa_engine/src/string.rs b/boa_engine/src/string.rs index 0a624184b49..d5a37af037c 100644 --- a/boa_engine/src/string.rs +++ b/boa_engine/src/string.rs @@ -7,157 +7,180 @@ use std::{ cell::Cell, hash::{Hash, Hasher}, marker::PhantomData, + num::NonZeroUsize, ops::Deref, - ptr::{copy_nonoverlapping, NonNull}, + ptr::copy_nonoverlapping, rc::Rc, }; -const CONSTANTS_ARRAY: [&str; 127] = [ +/// A fat pointer representing a static str. +struct StaticStr(*const u8, usize); + +impl StaticStr { + /// Build `Self` from a static string slice + #[inline] + const fn new(s: &'static str) -> Self { + Self(s.as_ptr(), s.len()) + } + + /// Returns the raw pointer of the str. + #[inline] + const fn ptr(&self) -> *const u8 { + self.0 + } + + /// Returns the length of the str. + #[inline] + const fn len(&self) -> usize { + self.1 + } +} + +const CONSTANTS_ARRAY: [StaticStr; 126] = [ // Empty string - "", + StaticStr::new(""), // Misc - ",", - ":", + StaticStr::new(","), + StaticStr::new(":"), // Generic use - "name", - "length", - "arguments", - "prototype", - "constructor", + StaticStr::new("name"), + StaticStr::new("length"), + StaticStr::new("arguments"), + StaticStr::new("prototype"), + StaticStr::new("constructor"), // typeof - "null", - "undefined", - "number", - "string", - "symbol", - "bigint", - "object", - "function", + StaticStr::new("null"), + StaticStr::new("undefined"), + StaticStr::new("number"), + StaticStr::new("string"), + StaticStr::new("symbol"), + StaticStr::new("bigint"), + StaticStr::new("object"), + StaticStr::new("function"), // Property descriptor - "value", - "get", - "set", - "writable", - "enumerable", - "configurable", + StaticStr::new("value"), + StaticStr::new("get"), + StaticStr::new("set"), + StaticStr::new("writable"), + StaticStr::new("enumerable"), + StaticStr::new("configurable"), // Object object - "Object", - "assing", - "create", - "toString", - "valueOf", - "is", - "seal", - "isSealed", - "freeze", - "isFrozen", - "keys", - "values", - "entries", + StaticStr::new("Object"), + StaticStr::new("assing"), + StaticStr::new("create"), + StaticStr::new("toString"), + StaticStr::new("valueOf"), + StaticStr::new("is"), + StaticStr::new("seal"), + StaticStr::new("isSealed"), + StaticStr::new("freeze"), + StaticStr::new("isFrozen"), + StaticStr::new("keys"), + StaticStr::new("values"), + StaticStr::new("entries"), // Function object - "Function", - "apply", - "bind", - "call", + StaticStr::new("Function"), + StaticStr::new("apply"), + StaticStr::new("bind"), + StaticStr::new("call"), // Array object - "Array", - "from", - "isArray", - "of", - "get [Symbol.species]", - "copyWithin", - "entries", - "every", - "fill", - "filter", - "find", - "findIndex", - "flat", - "flatMap", - "forEach", - "includes", - "indexOf", - "join", - "map", - "reduce", - "reduceRight", - "reverse", - "shift", - "slice", - "some", - "sort", - "unshift", - "push", - "pop", + StaticStr::new("Array"), + StaticStr::new("from"), + StaticStr::new("isArray"), + StaticStr::new("of"), + StaticStr::new("get [Symbol.species]"), + StaticStr::new("copyWithin"), + StaticStr::new("entries"), + StaticStr::new("every"), + StaticStr::new("fill"), + StaticStr::new("filter"), + StaticStr::new("find"), + StaticStr::new("findIndex"), + StaticStr::new("flat"), + StaticStr::new("flatMap"), + StaticStr::new("forEach"), + StaticStr::new("includes"), + StaticStr::new("indexOf"), + StaticStr::new("join"), + StaticStr::new("map"), + StaticStr::new("reduce"), + StaticStr::new("reduceRight"), + StaticStr::new("reverse"), + StaticStr::new("shift"), + StaticStr::new("slice"), + StaticStr::new("some"), + StaticStr::new("sort"), + StaticStr::new("unshift"), + StaticStr::new("push"), + StaticStr::new("pop"), // String object - "String", - "charAt", - "charCodeAt", - "concat", - "endsWith", - "includes", - "indexOf", - "lastIndexOf", - "match", - "matchAll", - "normalize", - "padEnd", - "padStart", - "repeat", - "replace", - "replaceAll", - "search", - "slice", - "split", - "startsWith", - "substring", - "toLowerString", - "toUpperString", - "trim", - "trimEnd", - "trimStart", + StaticStr::new("String"), + StaticStr::new("charAt"), + StaticStr::new("charCodeAt"), + StaticStr::new("concat"), + StaticStr::new("endsWith"), + StaticStr::new("includes"), + StaticStr::new("indexOf"), + StaticStr::new("lastIndexOf"), + StaticStr::new("match"), + StaticStr::new("matchAll"), + StaticStr::new("normalize"), + StaticStr::new("padEnd"), + StaticStr::new("padStart"), + StaticStr::new("repeat"), + StaticStr::new("replace"), + StaticStr::new("replaceAll"), + StaticStr::new("search"), + StaticStr::new("slice"), + StaticStr::new("split"), + StaticStr::new("startsWith"), + StaticStr::new("substring"), + StaticStr::new("toLowerString"), + StaticStr::new("toUpperString"), + StaticStr::new("trim"), + StaticStr::new("trimEnd"), + StaticStr::new("trimStart"), // Number object - "Number", + StaticStr::new("Number"), // Boolean object - "Boolean", + StaticStr::new("Boolean"), // RegExp object - "RegExp", - "exec", - "test", - "flags", - "index", - "lastIndex", + StaticStr::new("RegExp"), + StaticStr::new("exec"), + StaticStr::new("test"), + StaticStr::new("flags"), + StaticStr::new("index"), + StaticStr::new("lastIndex"), // Symbol object - "Symbol", - "for", - "keyFor", - "description", - "[Symbol.toPrimitive]", - "", + StaticStr::new("Symbol"), + StaticStr::new("for"), + StaticStr::new("keyFor"), + StaticStr::new("description"), + StaticStr::new("[Symbol.toPrimitive]"), // Map object - "Map", - "clear", - "delete", - "get", - "has", - "set", - "size", + StaticStr::new("Map"), + StaticStr::new("clear"), + StaticStr::new("delete"), + StaticStr::new("get"), + StaticStr::new("has"), + StaticStr::new("set"), + StaticStr::new("size"), // Set object - "Set", + StaticStr::new("Set"), // Reflect object - "Reflect", + StaticStr::new("Reflect"), // Error objects - "Error", - "TypeError", - "RangeError", - "SyntaxError", - "ReferenceError", - "EvalError", - "URIError", - "message", + StaticStr::new("Error"), + StaticStr::new("TypeError"), + StaticStr::new("RangeError"), + StaticStr::new("SyntaxError"), + StaticStr::new("ReferenceError"), + StaticStr::new("EvalError"), + StaticStr::new("URIError"), + StaticStr::new("message"), // Date object - "Date", - "toJSON", + StaticStr::new("Date"), + StaticStr::new("toJSON"), ]; const MAX_CONSTANT_STRING_LENGTH: usize = { @@ -186,10 +209,7 @@ thread_local! { let mut constants = FxHashSet::default(); for s in CONSTANTS_ARRAY.iter() { - let s = JsString { - inner: Inner::new(s), - _marker: PhantomData, - }; + let s = JsString::new_static(s); constants.insert(s); } @@ -215,7 +235,7 @@ struct Inner { impl Inner { /// Create a new `Inner` from `&str`. #[inline] - fn new(s: &str) -> NonNull { + fn new(s: &str) -> NonZeroUsize { // We get the layout of the `Inner` type and we extend by the size // of the string array. let inner_layout = Layout::new::(); @@ -245,12 +265,12 @@ impl Inner { }; // Safety: We already know it's not null, so this is safe. - unsafe { NonNull::new_unchecked(inner) } + unsafe { NonZeroUsize::new_unchecked(inner as usize) } } /// Concatenate array of strings. #[inline] - fn concat_array(strings: &[&str]) -> NonNull { + fn concat_array(strings: &[&str]) -> NonZeroUsize { let mut total_string_size = 0; for string in strings { total_string_size += string.len(); @@ -289,20 +309,20 @@ impl Inner { }; // Safety: We already know it's not null, so this is safe. - unsafe { NonNull::new_unchecked(inner) } + unsafe { NonZeroUsize::new_unchecked(inner as usize) } } /// Deallocate inner type with string data. #[inline] - unsafe fn dealloc(x: NonNull) { - let len = (*x.as_ptr()).len; + unsafe fn dealloc(x: *mut Self) { + let len = (*x).len; let inner_layout = Layout::new::(); let (layout, _offset) = inner_layout .extend(Layout::array::(len).expect("failed to create memory layout")) .expect("failed to extend memory layout"); - dealloc(x.as_ptr().cast::<_>(), layout); + dealloc(x.cast::<_>(), layout); } } @@ -314,18 +334,38 @@ impl Inner { /// pointer is kept, so its size is the size of a pointer. #[derive(Finalize)] pub struct JsString { - inner: NonNull, + /// This represents a raw pointer. It maybe a [`StaticStr`], or a [`Inner`]. + inner: NonZeroUsize, _marker: PhantomData>, } impl Default for JsString { #[inline] fn default() -> Self { - Self::new("") + Self::new_static(&CONSTANTS_ARRAY[0]) } } +/// Data stored in [`JsString`]. +enum InnerKind<'a> { + // A string allocated on the heap. + Heap(&'a Inner), + // A static string slice. + Static(&'static str), +} + impl JsString { + /// Create a new JavaScript string from [`StaticStr`]. + #[inline] + fn new_static(s: &StaticStr) -> Self { + Self { + // Safety: We already know it's not null, so this is safe. + // Set the first bit to 1, indicating that it is static. + inner: unsafe { NonZeroUsize::new_unchecked((s as *const _ as usize) | 1) }, + _marker: PhantomData, + } + } + /// Create an empty string, same as calling default. #[inline] pub fn empty() -> Self { @@ -390,25 +430,38 @@ impl JsString { /// Return the inner representation. #[inline] - fn inner(&self) -> &Inner { - unsafe { self.inner.as_ref() } + fn inner<'a>(&'a self) -> InnerKind<'a> { + let ptr = self.inner.get(); + // Check the first bit to 1. + match ptr & 1 { + 1 => unsafe { + let ptr = &*((ptr & !1) as *const StaticStr); + let slice = std::slice::from_raw_parts(ptr.ptr(), ptr.len()); + InnerKind::Static(std::str::from_utf8_unchecked(slice)) + }, + _ => InnerKind::Heap(unsafe { &*(ptr as *const Inner) }), + } } /// 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) + match self.inner() { + InnerKind::Heap(inner) => unsafe { + let slice = std::slice::from_raw_parts(inner.data.as_ptr(), inner.len); + std::str::from_utf8_unchecked(slice) + }, + InnerKind::Static(inner) => inner, } } /// Gets the number of `JsString`s which point to this allocation. #[inline] pub fn refcount(this: &Self) -> usize { - this.inner().refcount.get() + match this.inner() { + InnerKind::Heap(inner) => inner.refcount.get(), + InnerKind::Static(_inner) => 0, + } } /// Returns `true` if the two `JsString`s point to the same allocation (in a vein similar to [`ptr::eq`]). @@ -528,9 +581,9 @@ unsafe impl Trace for JsString { impl Clone for JsString { #[inline] fn clone(&self) -> Self { - let inner = self.inner(); - inner.refcount.set(inner.refcount.get() + 1); - + if let InnerKind::Heap(inner) = self.inner() { + inner.refcount.set(inner.refcount.get() + 1); + } Self { inner: self.inner, _marker: PhantomData, @@ -541,15 +594,16 @@ impl Clone for JsString { 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); + if let InnerKind::Heap(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.get() as *mut _); + } + } else { + inner.refcount.set(inner.refcount.get() - 1); } - } else { - inner.refcount.set(inner.refcount.get() - 1); } } } From 3598379cfdf3eace48e5811341a33592294e42af Mon Sep 17 00:00:00 2001 From: YXL Date: Sat, 23 Apr 2022 13:00:12 +0800 Subject: [PATCH 02/14] Add more elements to `CONSTANTS_ARRAY` Signed-off-by: YXL --- boa_engine/src/string.rs | 318 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 314 insertions(+), 4 deletions(-) diff --git a/boa_engine/src/string.rs b/boa_engine/src/string.rs index d5a37af037c..e2080d1ddd1 100644 --- a/boa_engine/src/string.rs +++ b/boa_engine/src/string.rs @@ -36,7 +36,7 @@ impl StaticStr { } } -const CONSTANTS_ARRAY: [StaticStr; 126] = [ +const CONSTANTS_ARRAY: [StaticStr; 426] = [ // Empty string StaticStr::new(""), // Misc @@ -48,6 +48,10 @@ const CONSTANTS_ARRAY: [StaticStr; 126] = [ StaticStr::new("arguments"), StaticStr::new("prototype"), StaticStr::new("constructor"), + StaticStr::new("return"), + StaticStr::new("throw"), + StaticStr::new("global"), + StaticStr::new("globalThis"), // typeof StaticStr::new("null"), StaticStr::new("undefined"), @@ -66,7 +70,7 @@ const CONSTANTS_ARRAY: [StaticStr; 126] = [ StaticStr::new("configurable"), // Object object StaticStr::new("Object"), - StaticStr::new("assing"), + StaticStr::new("assign"), StaticStr::new("create"), StaticStr::new("toString"), StaticStr::new("valueOf"), @@ -75,16 +79,38 @@ const CONSTANTS_ARRAY: [StaticStr; 126] = [ StaticStr::new("isSealed"), StaticStr::new("freeze"), StaticStr::new("isFrozen"), + StaticStr::new("preventExtensions"), + StaticStr::new("isExtensible"), + StaticStr::new("getOwnPropertyDescriptor"), + StaticStr::new("getOwnPropertyDescriptors"), + StaticStr::new("getOwnPropertyNames"), + StaticStr::new("getOwnPropertySymbols"), + StaticStr::new("hasOwnProperty"), + StaticStr::new("propertyIsEnumerable"), + StaticStr::new("isPrototypeOf"), + StaticStr::new("setPrototypeOf"), + StaticStr::new("getPrototypeOf"), + StaticStr::new("defineProperty"), + StaticStr::new("defineProperties"), + StaticStr::new("deleteProperty"), + StaticStr::new("construct"), + StaticStr::new("hasOwn"), + StaticStr::new("ownKeys"), StaticStr::new("keys"), StaticStr::new("values"), StaticStr::new("entries"), + StaticStr::new("fromEntries"), // Function object StaticStr::new("Function"), StaticStr::new("apply"), StaticStr::new("bind"), StaticStr::new("call"), + // Generator object + StaticStr::new("Generator"), + StaticStr::new("GeneratorFunction"), // Array object StaticStr::new("Array"), + StaticStr::new("at"), StaticStr::new("from"), StaticStr::new("isArray"), StaticStr::new("of"), @@ -96,6 +122,8 @@ const CONSTANTS_ARRAY: [StaticStr; 126] = [ StaticStr::new("filter"), StaticStr::new("find"), StaticStr::new("findIndex"), + StaticStr::new("findLast"), + StaticStr::new("findLastIndex"), StaticStr::new("flat"), StaticStr::new("flatMap"), StaticStr::new("forEach"), @@ -103,11 +131,13 @@ const CONSTANTS_ARRAY: [StaticStr; 126] = [ StaticStr::new("indexOf"), StaticStr::new("join"), StaticStr::new("map"), + StaticStr::new("next"), StaticStr::new("reduce"), StaticStr::new("reduceRight"), StaticStr::new("reverse"), StaticStr::new("shift"), StaticStr::new("slice"), + StaticStr::new("splice"), StaticStr::new("some"), StaticStr::new("sort"), StaticStr::new("unshift"), @@ -117,8 +147,11 @@ const CONSTANTS_ARRAY: [StaticStr; 126] = [ StaticStr::new("String"), StaticStr::new("charAt"), StaticStr::new("charCodeAt"), + StaticStr::new("codePointAt"), StaticStr::new("concat"), StaticStr::new("endsWith"), + StaticStr::new("fromCharCode"), + StaticStr::new("fromCodePoint"), StaticStr::new("includes"), StaticStr::new("indexOf"), StaticStr::new("lastIndexOf"), @@ -127,6 +160,7 @@ const CONSTANTS_ARRAY: [StaticStr; 126] = [ StaticStr::new("normalize"), StaticStr::new("padEnd"), StaticStr::new("padStart"), + StaticStr::new("raw"), StaticStr::new("repeat"), StaticStr::new("replace"), StaticStr::new("replaceAll"), @@ -134,16 +168,41 @@ const CONSTANTS_ARRAY: [StaticStr; 126] = [ StaticStr::new("slice"), StaticStr::new("split"), StaticStr::new("startsWith"), + StaticStr::new("substr"), StaticStr::new("substring"), - StaticStr::new("toLowerString"), - StaticStr::new("toUpperString"), + StaticStr::new("toLocaleString"), + StaticStr::new("toLowerCase"), + StaticStr::new("toUpperCase"), StaticStr::new("trim"), StaticStr::new("trimEnd"), StaticStr::new("trimStart"), // Number object StaticStr::new("Number"), + StaticStr::new("Infinity"), + StaticStr::new("NaN"), + StaticStr::new("parseInt"), + StaticStr::new("parseFloat"), + StaticStr::new("isFinite"), + StaticStr::new("isNaN"), + StaticStr::new("parseInt"), + StaticStr::new("EPSILON"), + StaticStr::new("MAX_SAFE_INTEGER"), + StaticStr::new("MIN_SAFE_INTEGER"), + StaticStr::new("MAX_VALUE"), + StaticStr::new("MIN_VALUE"), + StaticStr::new("NEGATIVE_INFINITY"), + StaticStr::new("POSITIVE_INFINITY"), + StaticStr::new("isSafeInteger"), + StaticStr::new("isInteger"), + StaticStr::new("toExponential"), + StaticStr::new("toFixed"), + StaticStr::new("toPrecision"), // Boolean object StaticStr::new("Boolean"), + // BigInt object + StaticStr::new("BigInt"), + StaticStr::new("asIntN"), + StaticStr::new("asUintN"), // RegExp object StaticStr::new("RegExp"), StaticStr::new("exec"), @@ -151,11 +210,47 @@ const CONSTANTS_ARRAY: [StaticStr; 126] = [ StaticStr::new("flags"), StaticStr::new("index"), StaticStr::new("lastIndex"), + StaticStr::new("hasIndices"), + StaticStr::new("ignoreCase"), + StaticStr::new("multiline"), + StaticStr::new("dotAll"), + StaticStr::new("unicode"), + StaticStr::new("sticky"), + StaticStr::new("source"), // Symbol object StaticStr::new("Symbol"), StaticStr::new("for"), StaticStr::new("keyFor"), StaticStr::new("description"), + StaticStr::new("asyncIterator"), + StaticStr::new("Symbol.asyncIterator"), + StaticStr::new("hasInstance"), + StaticStr::new("Symbol.hasInstance"), + StaticStr::new("isConcatSpreadable"), + StaticStr::new("Symbol.isConcatSpreadable"), + StaticStr::new("species"), + StaticStr::new("Symbol.species"), + StaticStr::new("unscopables"), + StaticStr::new("Symbol.unscopables"), + StaticStr::new("[Symbol.hasInstance]"), + StaticStr::new("iterator"), + StaticStr::new("Symbol.iterator"), + StaticStr::new("[Symbol.iterator]"), + StaticStr::new("Symbol.match"), + StaticStr::new("[Symbol.match]"), + StaticStr::new("Symbol.matchAll"), + StaticStr::new("[Symbol.matchAll]"), + StaticStr::new("Symbol.replace"), + StaticStr::new("[Symbol.replace]"), + StaticStr::new("Symbol.search"), + StaticStr::new("[Symbol.search]"), + StaticStr::new("Symbol.split"), + StaticStr::new("[Symbol.split]"), + StaticStr::new("toStringTag"), + StaticStr::new("Symbol.toStringTag"), + StaticStr::new("[Symbol.toStringTag]"), + StaticStr::new("toPrimitive"), + StaticStr::new("Symbol.toPrimitive"), StaticStr::new("[Symbol.toPrimitive]"), // Map object StaticStr::new("Map"), @@ -167,10 +262,15 @@ const CONSTANTS_ARRAY: [StaticStr; 126] = [ StaticStr::new("size"), // Set object StaticStr::new("Set"), + StaticStr::new("add"), // Reflect object StaticStr::new("Reflect"), + // Proxy object + StaticStr::new("Proxy"), + StaticStr::new("revocable"), // Error objects StaticStr::new("Error"), + StaticStr::new("AggregateError"), StaticStr::new("TypeError"), StaticStr::new("RangeError"), StaticStr::new("SyntaxError"), @@ -181,6 +281,216 @@ const CONSTANTS_ARRAY: [StaticStr; 126] = [ // Date object StaticStr::new("Date"), StaticStr::new("toJSON"), + StaticStr::new("getDate"), + StaticStr::new("getDay"), + StaticStr::new("getFullYear"), + StaticStr::new("getHours"), + StaticStr::new("getMilliseconds"), + StaticStr::new("getMinutes"), + StaticStr::new("getMonth"), + StaticStr::new("getSeconds"), + StaticStr::new("getTime"), + StaticStr::new("getYear"), + StaticStr::new("getTimezoneOffset"), + StaticStr::new("getUTCDate"), + StaticStr::new("getUTCDay"), + StaticStr::new("getUTCFullYear"), + StaticStr::new("getUTCHours"), + StaticStr::new("getUTCMilliseconds"), + StaticStr::new("getUTCMinutes"), + StaticStr::new("getUTCMonth"), + StaticStr::new("getUTCSeconds"), + StaticStr::new("setDate"), + StaticStr::new("setFullYear"), + StaticStr::new("setHours"), + StaticStr::new("setMilliseconds"), + StaticStr::new("setMinutes"), + StaticStr::new("setMonth"), + StaticStr::new("setSeconds"), + StaticStr::new("setYear"), + StaticStr::new("setTime"), + StaticStr::new("setUTCDate"), + StaticStr::new("setUTCFullYear"), + StaticStr::new("setUTCHours"), + StaticStr::new("setUTCMilliseconds"), + StaticStr::new("setUTCMinutes"), + StaticStr::new("setUTCMonth"), + StaticStr::new("setUTCSeconds"), + StaticStr::new("toDateString"), + StaticStr::new("toGMTString"), + StaticStr::new("toISOString"), + StaticStr::new("toTimeString"), + StaticStr::new("toUTCString"), + StaticStr::new("now"), + StaticStr::new("UTC"), + // JSON object + StaticStr::new("JSON"), + StaticStr::new("parse"), + StaticStr::new("stringify"), + // Math object + StaticStr::new("Math"), + StaticStr::new("LN10"), + StaticStr::new("LN2"), + StaticStr::new("LOG10E"), + StaticStr::new("LOG2E"), + StaticStr::new("PI"), + StaticStr::new("SQRT1_2"), + StaticStr::new("SQRT2"), + StaticStr::new("abs"), + StaticStr::new("acos"), + StaticStr::new("acosh"), + StaticStr::new("asin"), + StaticStr::new("asinh"), + StaticStr::new("atan"), + StaticStr::new("atanh"), + StaticStr::new("atan2"), + StaticStr::new("cbrt"), + StaticStr::new("ceil"), + StaticStr::new("clz32"), + StaticStr::new("cos"), + StaticStr::new("cosh"), + StaticStr::new("exp"), + StaticStr::new("expm1"), + StaticStr::new("floor"), + StaticStr::new("fround"), + StaticStr::new("hypot"), + StaticStr::new("imul"), + StaticStr::new("log"), + StaticStr::new("log1p"), + StaticStr::new("log10"), + StaticStr::new("log2"), + StaticStr::new("max"), + StaticStr::new("min"), + StaticStr::new("pow"), + StaticStr::new("random"), + StaticStr::new("round"), + StaticStr::new("sign"), + StaticStr::new("sin"), + StaticStr::new("sinh"), + StaticStr::new("sqrt"), + StaticStr::new("tan"), + StaticStr::new("tanh"), + StaticStr::new("trunc"), + // Intl object + StaticStr::new("Intl"), + StaticStr::new("DateTimeFormat"), + StaticStr::new("getCanonicalLocales"), + // TypedArray object + StaticStr::new("TypedArray"), + StaticStr::new("ArrayBuffer"), + StaticStr::new("Int8Array"), + StaticStr::new("Uint8Array"), + StaticStr::new("Uint8ClampedArray"), + StaticStr::new("Int16Array"), + StaticStr::new("Uint16Array"), + StaticStr::new("Int32Array"), + StaticStr::new("Uint32Array"), + StaticStr::new("BigInt64Array"), + StaticStr::new("BigUint64Array"), + StaticStr::new("Float32Array"), + StaticStr::new("Float64Array"), + StaticStr::new("BYTES_PER_ELEMENT"), + StaticStr::new("buffer"), + StaticStr::new("byteLength"), + StaticStr::new("byteOffset"), + StaticStr::new("isView"), + StaticStr::new("subarray"), + // DataView object + StaticStr::new("DataView"), + StaticStr::new("getBigInt64"), + StaticStr::new("getBigUint64"), + StaticStr::new("getFloat32"), + StaticStr::new("getFloat64"), + StaticStr::new("getInt8"), + StaticStr::new("getInt16"), + StaticStr::new("getInt32"), + StaticStr::new("getUint8"), + StaticStr::new("getUint16"), + StaticStr::new("getUint32"), + StaticStr::new("setBigInt64"), + StaticStr::new("setBigUint64"), + StaticStr::new("setFloat32"), + StaticStr::new("setFloat64"), + StaticStr::new("setInt8"), + StaticStr::new("setInt16"), + StaticStr::new("setInt32"), + StaticStr::new("setUint8"), + StaticStr::new("setUint16"), + StaticStr::new("setUint32"), + // Console object + StaticStr::new("console"), + StaticStr::new("assert"), + StaticStr::new("debug"), + StaticStr::new("error"), + StaticStr::new("info"), + StaticStr::new("trace"), + StaticStr::new("warn"), + StaticStr::new("exception"), + StaticStr::new("count"), + StaticStr::new("countReset"), + StaticStr::new("group"), + StaticStr::new("groupCollapsed"), + StaticStr::new("groupEnd"), + StaticStr::new("time"), + StaticStr::new("timeLog"), + StaticStr::new("timeEnd"), + StaticStr::new("dir"), + StaticStr::new("dirxml"), + // Minified name + StaticStr::new("a"), + StaticStr::new("b"), + StaticStr::new("c"), + StaticStr::new("d"), + StaticStr::new("e"), + StaticStr::new("f"), + StaticStr::new("g"), + StaticStr::new("h"), + StaticStr::new("i"), + StaticStr::new("j"), + StaticStr::new("k"), + StaticStr::new("l"), + StaticStr::new("m"), + StaticStr::new("n"), + StaticStr::new("o"), + StaticStr::new("p"), + StaticStr::new("q"), + StaticStr::new("r"), + StaticStr::new("s"), + StaticStr::new("t"), + StaticStr::new("u"), + StaticStr::new("v"), + StaticStr::new("w"), + StaticStr::new("x"), + StaticStr::new("y"), + StaticStr::new("z"), + StaticStr::new("A"), + StaticStr::new("B"), + StaticStr::new("C"), + StaticStr::new("D"), + StaticStr::new("E"), + StaticStr::new("F"), + StaticStr::new("G"), + StaticStr::new("H"), + StaticStr::new("I"), + StaticStr::new("J"), + StaticStr::new("K"), + StaticStr::new("L"), + StaticStr::new("M"), + StaticStr::new("N"), + StaticStr::new("O"), + StaticStr::new("P"), + StaticStr::new("Q"), + StaticStr::new("R"), + StaticStr::new("S"), + StaticStr::new("T"), + StaticStr::new("U"), + StaticStr::new("V"), + StaticStr::new("W"), + StaticStr::new("X"), + StaticStr::new("Y"), + StaticStr::new("Z"), + StaticStr::new("_"), + StaticStr::new("$"), ]; const MAX_CONSTANT_STRING_LENGTH: usize = { From 574f7224a0d78d364286dc011a28ae7db72d5b9e Mon Sep 17 00:00:00 2001 From: YXL Date: Sat, 23 Apr 2022 19:05:09 +0800 Subject: [PATCH 03/14] Remove `StaticStr` Signed-off-by: YXL --- boa_engine/src/string.rs | 914 +++++++++++++++++++-------------------- 1 file changed, 444 insertions(+), 470 deletions(-) diff --git a/boa_engine/src/string.rs b/boa_engine/src/string.rs index e2080d1ddd1..44d46e09c87 100644 --- a/boa_engine/src/string.rs +++ b/boa_engine/src/string.rs @@ -13,484 +13,461 @@ use std::{ rc::Rc, }; -/// A fat pointer representing a static str. -struct StaticStr(*const u8, usize); - -impl StaticStr { - /// Build `Self` from a static string slice - #[inline] - const fn new(s: &'static str) -> Self { - Self(s.as_ptr(), s.len()) - } - - /// Returns the raw pointer of the str. - #[inline] - const fn ptr(&self) -> *const u8 { - self.0 - } - - /// Returns the length of the str. - #[inline] - const fn len(&self) -> usize { - self.1 - } -} - -const CONSTANTS_ARRAY: [StaticStr; 426] = [ +const CONSTANTS_ARRAY: [&'static str; 426] = [ // Empty string - StaticStr::new(""), + "", // Misc - StaticStr::new(","), - StaticStr::new(":"), + ",", + ":", // Generic use - StaticStr::new("name"), - StaticStr::new("length"), - StaticStr::new("arguments"), - StaticStr::new("prototype"), - StaticStr::new("constructor"), - StaticStr::new("return"), - StaticStr::new("throw"), - StaticStr::new("global"), - StaticStr::new("globalThis"), + "name", + "length", + "arguments", + "prototype", + "constructor", + "return", + "throw", + "global", + "globalThis", // typeof - StaticStr::new("null"), - StaticStr::new("undefined"), - StaticStr::new("number"), - StaticStr::new("string"), - StaticStr::new("symbol"), - StaticStr::new("bigint"), - StaticStr::new("object"), - StaticStr::new("function"), + "null", + "undefined", + "number", + "string", + "symbol", + "bigint", + "object", + "function", // Property descriptor - StaticStr::new("value"), - StaticStr::new("get"), - StaticStr::new("set"), - StaticStr::new("writable"), - StaticStr::new("enumerable"), - StaticStr::new("configurable"), + "value", + "get", + "set", + "writable", + "enumerable", + "configurable", // Object object - StaticStr::new("Object"), - StaticStr::new("assign"), - StaticStr::new("create"), - StaticStr::new("toString"), - StaticStr::new("valueOf"), - StaticStr::new("is"), - StaticStr::new("seal"), - StaticStr::new("isSealed"), - StaticStr::new("freeze"), - StaticStr::new("isFrozen"), - StaticStr::new("preventExtensions"), - StaticStr::new("isExtensible"), - StaticStr::new("getOwnPropertyDescriptor"), - StaticStr::new("getOwnPropertyDescriptors"), - StaticStr::new("getOwnPropertyNames"), - StaticStr::new("getOwnPropertySymbols"), - StaticStr::new("hasOwnProperty"), - StaticStr::new("propertyIsEnumerable"), - StaticStr::new("isPrototypeOf"), - StaticStr::new("setPrototypeOf"), - StaticStr::new("getPrototypeOf"), - StaticStr::new("defineProperty"), - StaticStr::new("defineProperties"), - StaticStr::new("deleteProperty"), - StaticStr::new("construct"), - StaticStr::new("hasOwn"), - StaticStr::new("ownKeys"), - StaticStr::new("keys"), - StaticStr::new("values"), - StaticStr::new("entries"), - StaticStr::new("fromEntries"), + "Object", + "assign", + "create", + "toString", + "valueOf", + "is", + "seal", + "isSealed", + "freeze", + "isFrozen", + "preventExtensions", + "isExtensible", + "getOwnPropertyDescriptor", + "getOwnPropertyDescriptors", + "getOwnPropertyNames", + "getOwnPropertySymbols", + "hasOwnProperty", + "propertyIsEnumerable", + "isPrototypeOf", + "setPrototypeOf", + "getPrototypeOf", + "defineProperty", + "defineProperties", + "deleteProperty", + "construct", + "hasOwn", + "ownKeys", + "keys", + "values", + "entries", + "fromEntries", // Function object - StaticStr::new("Function"), - StaticStr::new("apply"), - StaticStr::new("bind"), - StaticStr::new("call"), + "Function", + "apply", + "bind", + "call", // Generator object - StaticStr::new("Generator"), - StaticStr::new("GeneratorFunction"), + "Generator", + "GeneratorFunction", // Array object - StaticStr::new("Array"), - StaticStr::new("at"), - StaticStr::new("from"), - StaticStr::new("isArray"), - StaticStr::new("of"), - StaticStr::new("get [Symbol.species]"), - StaticStr::new("copyWithin"), - StaticStr::new("entries"), - StaticStr::new("every"), - StaticStr::new("fill"), - StaticStr::new("filter"), - StaticStr::new("find"), - StaticStr::new("findIndex"), - StaticStr::new("findLast"), - StaticStr::new("findLastIndex"), - StaticStr::new("flat"), - StaticStr::new("flatMap"), - StaticStr::new("forEach"), - StaticStr::new("includes"), - StaticStr::new("indexOf"), - StaticStr::new("join"), - StaticStr::new("map"), - StaticStr::new("next"), - StaticStr::new("reduce"), - StaticStr::new("reduceRight"), - StaticStr::new("reverse"), - StaticStr::new("shift"), - StaticStr::new("slice"), - StaticStr::new("splice"), - StaticStr::new("some"), - StaticStr::new("sort"), - StaticStr::new("unshift"), - StaticStr::new("push"), - StaticStr::new("pop"), + "Array", + "at", + "from", + "isArray", + "of", + "get [Symbol.species]", + "copyWithin", + "entries", + "every", + "fill", + "filter", + "find", + "findIndex", + "findLast", + "findLastIndex", + "flat", + "flatMap", + "forEach", + "includes", + "indexOf", + "join", + "map", + "next", + "reduce", + "reduceRight", + "reverse", + "shift", + "slice", + "splice", + "some", + "sort", + "unshift", + "push", + "pop", // String object - StaticStr::new("String"), - StaticStr::new("charAt"), - StaticStr::new("charCodeAt"), - StaticStr::new("codePointAt"), - StaticStr::new("concat"), - StaticStr::new("endsWith"), - StaticStr::new("fromCharCode"), - StaticStr::new("fromCodePoint"), - StaticStr::new("includes"), - StaticStr::new("indexOf"), - StaticStr::new("lastIndexOf"), - StaticStr::new("match"), - StaticStr::new("matchAll"), - StaticStr::new("normalize"), - StaticStr::new("padEnd"), - StaticStr::new("padStart"), - StaticStr::new("raw"), - StaticStr::new("repeat"), - StaticStr::new("replace"), - StaticStr::new("replaceAll"), - StaticStr::new("search"), - StaticStr::new("slice"), - StaticStr::new("split"), - StaticStr::new("startsWith"), - StaticStr::new("substr"), - StaticStr::new("substring"), - StaticStr::new("toLocaleString"), - StaticStr::new("toLowerCase"), - StaticStr::new("toUpperCase"), - StaticStr::new("trim"), - StaticStr::new("trimEnd"), - StaticStr::new("trimStart"), + "String", + "charAt", + "charCodeAt", + "codePointAt", + "concat", + "endsWith", + "fromCharCode", + "fromCodePoint", + "includes", + "indexOf", + "lastIndexOf", + "match", + "matchAll", + "normalize", + "padEnd", + "padStart", + "raw", + "repeat", + "replace", + "replaceAll", + "search", + "slice", + "split", + "startsWith", + "substr", + "substring", + "toLocaleString", + "toLowerCase", + "toUpperCase", + "trim", + "trimEnd", + "trimStart", // Number object - StaticStr::new("Number"), - StaticStr::new("Infinity"), - StaticStr::new("NaN"), - StaticStr::new("parseInt"), - StaticStr::new("parseFloat"), - StaticStr::new("isFinite"), - StaticStr::new("isNaN"), - StaticStr::new("parseInt"), - StaticStr::new("EPSILON"), - StaticStr::new("MAX_SAFE_INTEGER"), - StaticStr::new("MIN_SAFE_INTEGER"), - StaticStr::new("MAX_VALUE"), - StaticStr::new("MIN_VALUE"), - StaticStr::new("NEGATIVE_INFINITY"), - StaticStr::new("POSITIVE_INFINITY"), - StaticStr::new("isSafeInteger"), - StaticStr::new("isInteger"), - StaticStr::new("toExponential"), - StaticStr::new("toFixed"), - StaticStr::new("toPrecision"), + "Number", + "Infinity", + "NaN", + "parseInt", + "parseFloat", + "isFinite", + "isNaN", + "parseInt", + "EPSILON", + "MAX_SAFE_INTEGER", + "MIN_SAFE_INTEGER", + "MAX_VALUE", + "MIN_VALUE", + "NEGATIVE_INFINITY", + "POSITIVE_INFINITY", + "isSafeInteger", + "isInteger", + "toExponential", + "toFixed", + "toPrecision", // Boolean object - StaticStr::new("Boolean"), + "Boolean", // BigInt object - StaticStr::new("BigInt"), - StaticStr::new("asIntN"), - StaticStr::new("asUintN"), + "BigInt", + "asIntN", + "asUintN", // RegExp object - StaticStr::new("RegExp"), - StaticStr::new("exec"), - StaticStr::new("test"), - StaticStr::new("flags"), - StaticStr::new("index"), - StaticStr::new("lastIndex"), - StaticStr::new("hasIndices"), - StaticStr::new("ignoreCase"), - StaticStr::new("multiline"), - StaticStr::new("dotAll"), - StaticStr::new("unicode"), - StaticStr::new("sticky"), - StaticStr::new("source"), + "RegExp", + "exec", + "test", + "flags", + "index", + "lastIndex", + "hasIndices", + "ignoreCase", + "multiline", + "dotAll", + "unicode", + "sticky", + "source", // Symbol object - StaticStr::new("Symbol"), - StaticStr::new("for"), - StaticStr::new("keyFor"), - StaticStr::new("description"), - StaticStr::new("asyncIterator"), - StaticStr::new("Symbol.asyncIterator"), - StaticStr::new("hasInstance"), - StaticStr::new("Symbol.hasInstance"), - StaticStr::new("isConcatSpreadable"), - StaticStr::new("Symbol.isConcatSpreadable"), - StaticStr::new("species"), - StaticStr::new("Symbol.species"), - StaticStr::new("unscopables"), - StaticStr::new("Symbol.unscopables"), - StaticStr::new("[Symbol.hasInstance]"), - StaticStr::new("iterator"), - StaticStr::new("Symbol.iterator"), - StaticStr::new("[Symbol.iterator]"), - StaticStr::new("Symbol.match"), - StaticStr::new("[Symbol.match]"), - StaticStr::new("Symbol.matchAll"), - StaticStr::new("[Symbol.matchAll]"), - StaticStr::new("Symbol.replace"), - StaticStr::new("[Symbol.replace]"), - StaticStr::new("Symbol.search"), - StaticStr::new("[Symbol.search]"), - StaticStr::new("Symbol.split"), - StaticStr::new("[Symbol.split]"), - StaticStr::new("toStringTag"), - StaticStr::new("Symbol.toStringTag"), - StaticStr::new("[Symbol.toStringTag]"), - StaticStr::new("toPrimitive"), - StaticStr::new("Symbol.toPrimitive"), - StaticStr::new("[Symbol.toPrimitive]"), + "Symbol", + "for", + "keyFor", + "description", + "asyncIterator", + "Symbol.asyncIterator", + "hasInstance", + "Symbol.hasInstance", + "isConcatSpreadable", + "Symbol.isConcatSpreadable", + "species", + "Symbol.species", + "unscopables", + "Symbol.unscopables", + "[Symbol.hasInstance]", + "iterator", + "Symbol.iterator", + "[Symbol.iterator]", + "Symbol.match", + "[Symbol.match]", + "Symbol.matchAll", + "[Symbol.matchAll]", + "Symbol.replace", + "[Symbol.replace]", + "Symbol.search", + "[Symbol.search]", + "Symbol.split", + "[Symbol.split]", + "toStringTag", + "Symbol.toStringTag", + "[Symbol.toStringTag]", + "toPrimitive", + "Symbol.toPrimitive", + "[Symbol.toPrimitive]", // Map object - StaticStr::new("Map"), - StaticStr::new("clear"), - StaticStr::new("delete"), - StaticStr::new("get"), - StaticStr::new("has"), - StaticStr::new("set"), - StaticStr::new("size"), + "Map", + "clear", + "delete", + "get", + "has", + "set", + "size", // Set object - StaticStr::new("Set"), - StaticStr::new("add"), + "Set", + "add", // Reflect object - StaticStr::new("Reflect"), + "Reflect", // Proxy object - StaticStr::new("Proxy"), - StaticStr::new("revocable"), + "Proxy", + "revocable", // Error objects - StaticStr::new("Error"), - StaticStr::new("AggregateError"), - StaticStr::new("TypeError"), - StaticStr::new("RangeError"), - StaticStr::new("SyntaxError"), - StaticStr::new("ReferenceError"), - StaticStr::new("EvalError"), - StaticStr::new("URIError"), - StaticStr::new("message"), + "Error", + "AggregateError", + "TypeError", + "RangeError", + "SyntaxError", + "ReferenceError", + "EvalError", + "URIError", + "message", // Date object - StaticStr::new("Date"), - StaticStr::new("toJSON"), - StaticStr::new("getDate"), - StaticStr::new("getDay"), - StaticStr::new("getFullYear"), - StaticStr::new("getHours"), - StaticStr::new("getMilliseconds"), - StaticStr::new("getMinutes"), - StaticStr::new("getMonth"), - StaticStr::new("getSeconds"), - StaticStr::new("getTime"), - StaticStr::new("getYear"), - StaticStr::new("getTimezoneOffset"), - StaticStr::new("getUTCDate"), - StaticStr::new("getUTCDay"), - StaticStr::new("getUTCFullYear"), - StaticStr::new("getUTCHours"), - StaticStr::new("getUTCMilliseconds"), - StaticStr::new("getUTCMinutes"), - StaticStr::new("getUTCMonth"), - StaticStr::new("getUTCSeconds"), - StaticStr::new("setDate"), - StaticStr::new("setFullYear"), - StaticStr::new("setHours"), - StaticStr::new("setMilliseconds"), - StaticStr::new("setMinutes"), - StaticStr::new("setMonth"), - StaticStr::new("setSeconds"), - StaticStr::new("setYear"), - StaticStr::new("setTime"), - StaticStr::new("setUTCDate"), - StaticStr::new("setUTCFullYear"), - StaticStr::new("setUTCHours"), - StaticStr::new("setUTCMilliseconds"), - StaticStr::new("setUTCMinutes"), - StaticStr::new("setUTCMonth"), - StaticStr::new("setUTCSeconds"), - StaticStr::new("toDateString"), - StaticStr::new("toGMTString"), - StaticStr::new("toISOString"), - StaticStr::new("toTimeString"), - StaticStr::new("toUTCString"), - StaticStr::new("now"), - StaticStr::new("UTC"), + "Date", + "toJSON", + "getDate", + "getDay", + "getFullYear", + "getHours", + "getMilliseconds", + "getMinutes", + "getMonth", + "getSeconds", + "getTime", + "getYear", + "getTimezoneOffset", + "getUTCDate", + "getUTCDay", + "getUTCFullYear", + "getUTCHours", + "getUTCMilliseconds", + "getUTCMinutes", + "getUTCMonth", + "getUTCSeconds", + "setDate", + "setFullYear", + "setHours", + "setMilliseconds", + "setMinutes", + "setMonth", + "setSeconds", + "setYear", + "setTime", + "setUTCDate", + "setUTCFullYear", + "setUTCHours", + "setUTCMilliseconds", + "setUTCMinutes", + "setUTCMonth", + "setUTCSeconds", + "toDateString", + "toGMTString", + "toISOString", + "toTimeString", + "toUTCString", + "now", + "UTC", // JSON object - StaticStr::new("JSON"), - StaticStr::new("parse"), - StaticStr::new("stringify"), + "JSON", + "parse", + "stringify", // Math object - StaticStr::new("Math"), - StaticStr::new("LN10"), - StaticStr::new("LN2"), - StaticStr::new("LOG10E"), - StaticStr::new("LOG2E"), - StaticStr::new("PI"), - StaticStr::new("SQRT1_2"), - StaticStr::new("SQRT2"), - StaticStr::new("abs"), - StaticStr::new("acos"), - StaticStr::new("acosh"), - StaticStr::new("asin"), - StaticStr::new("asinh"), - StaticStr::new("atan"), - StaticStr::new("atanh"), - StaticStr::new("atan2"), - StaticStr::new("cbrt"), - StaticStr::new("ceil"), - StaticStr::new("clz32"), - StaticStr::new("cos"), - StaticStr::new("cosh"), - StaticStr::new("exp"), - StaticStr::new("expm1"), - StaticStr::new("floor"), - StaticStr::new("fround"), - StaticStr::new("hypot"), - StaticStr::new("imul"), - StaticStr::new("log"), - StaticStr::new("log1p"), - StaticStr::new("log10"), - StaticStr::new("log2"), - StaticStr::new("max"), - StaticStr::new("min"), - StaticStr::new("pow"), - StaticStr::new("random"), - StaticStr::new("round"), - StaticStr::new("sign"), - StaticStr::new("sin"), - StaticStr::new("sinh"), - StaticStr::new("sqrt"), - StaticStr::new("tan"), - StaticStr::new("tanh"), - StaticStr::new("trunc"), + "Math", + "LN10", + "LN2", + "LOG10E", + "LOG2E", + "PI", + "SQRT1_2", + "SQRT2", + "abs", + "acos", + "acosh", + "asin", + "asinh", + "atan", + "atanh", + "atan2", + "cbrt", + "ceil", + "clz32", + "cos", + "cosh", + "exp", + "expm1", + "floor", + "fround", + "hypot", + "imul", + "log", + "log1p", + "log10", + "log2", + "max", + "min", + "pow", + "random", + "round", + "sign", + "sin", + "sinh", + "sqrt", + "tan", + "tanh", + "trunc", // Intl object - StaticStr::new("Intl"), - StaticStr::new("DateTimeFormat"), - StaticStr::new("getCanonicalLocales"), + "Intl", + "DateTimeFormat", + "getCanonicalLocales", // TypedArray object - StaticStr::new("TypedArray"), - StaticStr::new("ArrayBuffer"), - StaticStr::new("Int8Array"), - StaticStr::new("Uint8Array"), - StaticStr::new("Uint8ClampedArray"), - StaticStr::new("Int16Array"), - StaticStr::new("Uint16Array"), - StaticStr::new("Int32Array"), - StaticStr::new("Uint32Array"), - StaticStr::new("BigInt64Array"), - StaticStr::new("BigUint64Array"), - StaticStr::new("Float32Array"), - StaticStr::new("Float64Array"), - StaticStr::new("BYTES_PER_ELEMENT"), - StaticStr::new("buffer"), - StaticStr::new("byteLength"), - StaticStr::new("byteOffset"), - StaticStr::new("isView"), - StaticStr::new("subarray"), + "TypedArray", + "ArrayBuffer", + "Int8Array", + "Uint8Array", + "Uint8ClampedArray", + "Int16Array", + "Uint16Array", + "Int32Array", + "Uint32Array", + "BigInt64Array", + "BigUint64Array", + "Float32Array", + "Float64Array", + "BYTES_PER_ELEMENT", + "buffer", + "byteLength", + "byteOffset", + "isView", + "subarray", // DataView object - StaticStr::new("DataView"), - StaticStr::new("getBigInt64"), - StaticStr::new("getBigUint64"), - StaticStr::new("getFloat32"), - StaticStr::new("getFloat64"), - StaticStr::new("getInt8"), - StaticStr::new("getInt16"), - StaticStr::new("getInt32"), - StaticStr::new("getUint8"), - StaticStr::new("getUint16"), - StaticStr::new("getUint32"), - StaticStr::new("setBigInt64"), - StaticStr::new("setBigUint64"), - StaticStr::new("setFloat32"), - StaticStr::new("setFloat64"), - StaticStr::new("setInt8"), - StaticStr::new("setInt16"), - StaticStr::new("setInt32"), - StaticStr::new("setUint8"), - StaticStr::new("setUint16"), - StaticStr::new("setUint32"), + "DataView", + "getBigInt64", + "getBigUint64", + "getFloat32", + "getFloat64", + "getInt8", + "getInt16", + "getInt32", + "getUint8", + "getUint16", + "getUint32", + "setBigInt64", + "setBigUint64", + "setFloat32", + "setFloat64", + "setInt8", + "setInt16", + "setInt32", + "setUint8", + "setUint16", + "setUint32", // Console object - StaticStr::new("console"), - StaticStr::new("assert"), - StaticStr::new("debug"), - StaticStr::new("error"), - StaticStr::new("info"), - StaticStr::new("trace"), - StaticStr::new("warn"), - StaticStr::new("exception"), - StaticStr::new("count"), - StaticStr::new("countReset"), - StaticStr::new("group"), - StaticStr::new("groupCollapsed"), - StaticStr::new("groupEnd"), - StaticStr::new("time"), - StaticStr::new("timeLog"), - StaticStr::new("timeEnd"), - StaticStr::new("dir"), - StaticStr::new("dirxml"), + "console", + "assert", + "debug", + "error", + "info", + "trace", + "warn", + "exception", + "count", + "countReset", + "group", + "groupCollapsed", + "groupEnd", + "time", + "timeLog", + "timeEnd", + "dir", + "dirxml", // Minified name - StaticStr::new("a"), - StaticStr::new("b"), - StaticStr::new("c"), - StaticStr::new("d"), - StaticStr::new("e"), - StaticStr::new("f"), - StaticStr::new("g"), - StaticStr::new("h"), - StaticStr::new("i"), - StaticStr::new("j"), - StaticStr::new("k"), - StaticStr::new("l"), - StaticStr::new("m"), - StaticStr::new("n"), - StaticStr::new("o"), - StaticStr::new("p"), - StaticStr::new("q"), - StaticStr::new("r"), - StaticStr::new("s"), - StaticStr::new("t"), - StaticStr::new("u"), - StaticStr::new("v"), - StaticStr::new("w"), - StaticStr::new("x"), - StaticStr::new("y"), - StaticStr::new("z"), - StaticStr::new("A"), - StaticStr::new("B"), - StaticStr::new("C"), - StaticStr::new("D"), - StaticStr::new("E"), - StaticStr::new("F"), - StaticStr::new("G"), - StaticStr::new("H"), - StaticStr::new("I"), - StaticStr::new("J"), - StaticStr::new("K"), - StaticStr::new("L"), - StaticStr::new("M"), - StaticStr::new("N"), - StaticStr::new("O"), - StaticStr::new("P"), - StaticStr::new("Q"), - StaticStr::new("R"), - StaticStr::new("S"), - StaticStr::new("T"), - StaticStr::new("U"), - StaticStr::new("V"), - StaticStr::new("W"), - StaticStr::new("X"), - StaticStr::new("Y"), - StaticStr::new("Z"), - StaticStr::new("_"), - StaticStr::new("$"), + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", + "_", + "$", ]; const MAX_CONSTANT_STRING_LENGTH: usize = { @@ -516,10 +493,11 @@ unsafe fn try_alloc(layout: Layout) -> *mut u8 { thread_local! { static CONSTANTS: FxHashSet = { - let mut constants = FxHashSet::default(); + let len = CONSTANTS_ARRAY.len(); + let mut constants = FxHashSet::with_capacity_and_hasher(len, Default::default()); - for s in CONSTANTS_ARRAY.iter() { - let s = JsString::new_static(s); + for idx in 0..len { + let s = JsString::new_static(idx); constants.insert(s); } @@ -545,7 +523,7 @@ struct Inner { impl Inner { /// Create a new `Inner` from `&str`. #[inline] - fn new(s: &str) -> NonZeroUsize { + fn new(s: &str) -> *mut Self { // We get the layout of the `Inner` type and we extend by the size // of the string array. let inner_layout = Layout::new::(); @@ -575,7 +553,7 @@ impl Inner { }; // Safety: We already know it's not null, so this is safe. - unsafe { NonZeroUsize::new_unchecked(inner as usize) } + inner } /// Concatenate array of strings. @@ -644,7 +622,8 @@ impl Inner { /// pointer is kept, so its size is the size of a pointer. #[derive(Finalize)] pub struct JsString { - /// This represents a raw pointer. It maybe a [`StaticStr`], or a [`Inner`]. + /// This represents a raw pointer. It maybe an index of [`CONSTANTS_ARRAY`], + /// or a [`Inner`]. Use the first bit as the flag. inner: NonZeroUsize, _marker: PhantomData>, } @@ -652,7 +631,7 @@ pub struct JsString { impl Default for JsString { #[inline] fn default() -> Self { - Self::new_static(&CONSTANTS_ARRAY[0]) + Self::new_static(0) } } @@ -665,13 +644,13 @@ enum InnerKind<'a> { } impl JsString { - /// Create a new JavaScript string from [`StaticStr`]. + /// Create a new JavaScript string from an index of [`CONSTANTS_ARRAY`]. #[inline] - fn new_static(s: &StaticStr) -> Self { + fn new_static(idx: usize) -> Self { Self { // Safety: We already know it's not null, so this is safe. // Set the first bit to 1, indicating that it is static. - inner: unsafe { NonZeroUsize::new_unchecked((s as *const _ as usize) | 1) }, + inner: unsafe { NonZeroUsize::new_unchecked((idx << 1) | 1) }, _marker: PhantomData, } } @@ -694,7 +673,7 @@ impl JsString { } Self { - inner: Inner::new(s), + inner: unsafe { NonZeroUsize::new_unchecked(Inner::new(s) as usize) }, _marker: PhantomData, } } @@ -740,16 +719,11 @@ impl JsString { /// Return the inner representation. #[inline] - fn inner<'a>(&'a self) -> InnerKind<'a> { - let ptr = self.inner.get(); + fn inner(&self) -> InnerKind<'_> { // Check the first bit to 1. - match ptr & 1 { - 1 => unsafe { - let ptr = &*((ptr & !1) as *const StaticStr); - let slice = std::slice::from_raw_parts(ptr.ptr(), ptr.len()); - InnerKind::Static(std::str::from_utf8_unchecked(slice)) - }, - _ => InnerKind::Heap(unsafe { &*(ptr as *const Inner) }), + match self.inner.get() & 1 { + 1 => InnerKind::Static(CONSTANTS_ARRAY[self.inner.get() >> 1]), + _ => InnerKind::Heap(unsafe { &*(self.inner.get() as *const _) }), } } From effcaa404c5bc8d9437847c2ee945dd5f1042566 Mon Sep 17 00:00:00 2001 From: YXL Date: Sat, 23 Apr 2022 21:18:52 +0800 Subject: [PATCH 04/14] fix: Clippy lints Signed-off-by: YXL --- boa_engine/src/bigint.rs | 4 ++-- boa_engine/src/string.rs | 27 +++++++++++---------------- boa_engine/src/value/mod.rs | 1 - 3 files changed, 13 insertions(+), 19 deletions(-) diff --git a/boa_engine/src/bigint.rs b/boa_engine/src/bigint.rs index e276f5bc6c8..61463e51d0c 100644 --- a/boa_engine/src/bigint.rs +++ b/boa_engine/src/bigint.rs @@ -180,7 +180,7 @@ impl JsBigInt { let inner = if n > 0 { x.inner.as_ref().clone().shr(n as usize) } else { - x.inner.as_ref().clone().shl(n.abs() as usize) + x.inner.as_ref().clone().shl(n.unsigned_abs()) }; Ok(Self::new(inner)) @@ -195,7 +195,7 @@ impl JsBigInt { let inner = if n > 0 { x.inner.as_ref().clone().shl(n as usize) } else { - x.inner.as_ref().clone().shr(n.abs() as usize) + x.inner.as_ref().clone().shr(n.unsigned_abs()) }; Ok(Self::new(inner)) diff --git a/boa_engine/src/string.rs b/boa_engine/src/string.rs index 44d46e09c87..0191cdac621 100644 --- a/boa_engine/src/string.rs +++ b/boa_engine/src/string.rs @@ -1,10 +1,11 @@ use crate::builtins::string::is_trimmable_whitespace; use boa_gc::{unsafe_empty_trace, Finalize, Trace}; -use rustc_hash::FxHashSet; +use rustc_hash::{FxHashSet, FxHasher}; use std::{ alloc::{alloc, dealloc, handle_alloc_error, Layout}, borrow::Borrow, cell::Cell, + hash::BuildHasherDefault, hash::{Hash, Hasher}, marker::PhantomData, num::NonZeroUsize, @@ -13,7 +14,7 @@ use std::{ rc::Rc, }; -const CONSTANTS_ARRAY: [&'static str; 426] = [ +const CONSTANTS_ARRAY: [&str; 426] = [ // Empty string "", // Misc @@ -494,7 +495,7 @@ unsafe fn try_alloc(layout: Layout) -> *mut u8 { thread_local! { static CONSTANTS: FxHashSet = { let len = CONSTANTS_ARRAY.len(); - let mut constants = FxHashSet::with_capacity_and_hasher(len, Default::default()); + let mut constants = FxHashSet::with_capacity_and_hasher(len, BuildHasherDefault::::default()); for idx in 0..len { let s = JsString::new_static(idx); @@ -531,7 +532,7 @@ impl Inner { .extend(Layout::array::(s.len()).expect("failed to create memory layout")) .expect("failed to extend memory layout"); - let inner = unsafe { + unsafe { let inner = try_alloc(layout).cast::(); // Write the first part, the Inner. @@ -550,15 +551,12 @@ impl Inner { copy_nonoverlapping(s.as_ptr(), data, s.len()); inner - }; - - // Safety: We already know it's not null, so this is safe. - inner + } } /// Concatenate array of strings. #[inline] - fn concat_array(strings: &[&str]) -> NonZeroUsize { + fn concat_array(strings: &[&str]) -> *mut Self { let mut total_string_size = 0; for string in strings { total_string_size += string.len(); @@ -571,7 +569,7 @@ impl Inner { .extend(Layout::array::(total_string_size).expect("failed to create memory layout")) .expect("failed to extend memory layout"); - let inner = unsafe { + unsafe { let inner = try_alloc(layout).cast::(); // Write the first part, the Inner. @@ -594,10 +592,7 @@ impl Inner { } inner - }; - - // Safety: We already know it's not null, so this is safe. - unsafe { NonZeroUsize::new_unchecked(inner as usize) } + } } /// Deallocate inner type with string data. @@ -688,7 +683,7 @@ impl JsString { let y = y.as_ref(); let this = Self { - inner: Inner::concat_array(&[x, y]), + inner: unsafe { NonZeroUsize::new_unchecked(Inner::concat_array(&[x, y]) as usize) }, _marker: PhantomData, }; @@ -704,7 +699,7 @@ impl JsString { /// Concatenate array of string. pub fn concat_array(strings: &[&str]) -> Self { let this = Self { - inner: Inner::concat_array(strings), + inner: unsafe { NonZeroUsize::new_unchecked(Inner::concat_array(strings) as usize) }, _marker: PhantomData, }; diff --git a/boa_engine/src/value/mod.rs b/boa_engine/src/value/mod.rs index 2fd12402887..719a6689504 100644 --- a/boa_engine/src/value/mod.rs +++ b/boa_engine/src/value/mod.rs @@ -278,7 +278,6 @@ impl JsValue { /// [spec]: https://tc39.es/ecma262/#sec-toboolean pub fn to_boolean(&self) -> bool { match *self { - Self::Undefined | Self::Null => false, Self::Symbol(_) | Self::Object(_) => true, Self::String(ref s) if !s.is_empty() => true, Self::Rational(n) if n != 0.0 && !n.is_nan() => true, From 119e547dd7a1998cb1d5f4cd11ba32e6aee6be65 Mon Sep 17 00:00:00 2001 From: YXL Date: Sun, 24 Apr 2022 10:40:34 +0800 Subject: [PATCH 05/14] Make meaning of `inner` more clear Signed-off-by: YXL --- boa_engine/src/string.rs | 70 ++++++++++++++++++++++++++++++++-------- 1 file changed, 57 insertions(+), 13 deletions(-) diff --git a/boa_engine/src/string.rs b/boa_engine/src/string.rs index 0191cdac621..1acf8f99179 100644 --- a/boa_engine/src/string.rs +++ b/boa_engine/src/string.rs @@ -617,12 +617,56 @@ impl Inner { /// pointer is kept, so its size is the size of a pointer. #[derive(Finalize)] pub struct JsString { - /// This represents a raw pointer. It maybe an index of [`CONSTANTS_ARRAY`], - /// or a [`Inner`]. Use the first bit as the flag. - inner: NonZeroUsize, + inner: Flag, _marker: PhantomData>, } +/// It maybe an index of [`CONSTANTS_ARRAY`], or a raw pointer of [`Inner`]. +/// Use the first bit as the flag. +/// Detail: https://en.wikipedia.org/wiki/Tagged_pointer +#[repr(transparent)] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +struct Flag(NonZeroUsize); + +impl Flag { + #[inline] + fn new_heap(inner: *mut Inner) -> Self { + // Safety: We already know it's not null, so this is safe. + Self(unsafe { NonZeroUsize::new_unchecked(inner as usize) }) + } + + /// Set the first bit to 1, indicating that it is static. Store the index + /// in 1..63 bits. + #[inline] + const fn new_static(idx: usize) -> Self { + // Safety: We already know it's not null, so this is safe. + Self(unsafe { NonZeroUsize::new_unchecked((idx << 1) | 1) }) + } + + /// Check if the first bit is 1. + #[inline] + const fn is_static(&self) -> bool { + self.0.get() & 1 == 1 + } + + /// # Safety + /// + /// It maybe a static string. + #[inline] + const unsafe fn get_heap_unchecked(&self) -> *mut Inner { + self.0.get() as *mut _ + } + + /// # Safety + /// + /// It maybe a string allocated on the heap. + #[inline] + const unsafe fn get_static_unchecked(&self) -> &'static str { + // shift right to get the index. + CONSTANTS_ARRAY[self.0.get() >> 1] + } +} + impl Default for JsString { #[inline] fn default() -> Self { @@ -643,9 +687,7 @@ impl JsString { #[inline] fn new_static(idx: usize) -> Self { Self { - // Safety: We already know it's not null, so this is safe. - // Set the first bit to 1, indicating that it is static. - inner: unsafe { NonZeroUsize::new_unchecked((idx << 1) | 1) }, + inner: Flag::new_static(idx), _marker: PhantomData, } } @@ -668,7 +710,7 @@ impl JsString { } Self { - inner: unsafe { NonZeroUsize::new_unchecked(Inner::new(s) as usize) }, + inner: Flag::new_heap(Inner::new(s)), _marker: PhantomData, } } @@ -683,7 +725,7 @@ impl JsString { let y = y.as_ref(); let this = Self { - inner: unsafe { NonZeroUsize::new_unchecked(Inner::concat_array(&[x, y]) as usize) }, + inner: Flag::new_heap(Inner::concat_array(&[x, y])), _marker: PhantomData, }; @@ -699,7 +741,7 @@ impl JsString { /// Concatenate array of string. pub fn concat_array(strings: &[&str]) -> Self { let this = Self { - inner: unsafe { NonZeroUsize::new_unchecked(Inner::concat_array(strings) as usize) }, + inner: Flag::new_heap(Inner::concat_array(strings)), _marker: PhantomData, }; @@ -716,9 +758,11 @@ impl JsString { #[inline] fn inner(&self) -> InnerKind<'_> { // Check the first bit to 1. - match self.inner.get() & 1 { - 1 => InnerKind::Static(CONSTANTS_ARRAY[self.inner.get() >> 1]), - _ => InnerKind::Heap(unsafe { &*(self.inner.get() as *const _) }), + match self.inner.is_static() { + // Safety: We already checked. + true => InnerKind::Static(unsafe { self.inner.get_static_unchecked() }), + // Safety: We already checked. + _ => InnerKind::Heap(unsafe { &*self.inner.get_heap_unchecked() }), } } @@ -878,7 +922,7 @@ impl Drop for JsString { // 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.get() as *mut _); + Inner::dealloc(self.inner.get_heap_unchecked()); } } else { inner.refcount.set(inner.refcount.get() - 1); From 1b2236e95299aaf2baafdf52602ba120a72a44fd Mon Sep 17 00:00:00 2001 From: YXL Date: Sun, 24 Apr 2022 11:25:21 +0800 Subject: [PATCH 06/14] Fix clippy lints Signed-off-by: YXL --- boa_engine/src/string.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/boa_engine/src/string.rs b/boa_engine/src/string.rs index 1acf8f99179..30a0cfccd4b 100644 --- a/boa_engine/src/string.rs +++ b/boa_engine/src/string.rs @@ -623,7 +623,7 @@ pub struct JsString { /// It maybe an index of [`CONSTANTS_ARRAY`], or a raw pointer of [`Inner`]. /// Use the first bit as the flag. -/// Detail: https://en.wikipedia.org/wiki/Tagged_pointer +/// Detail: #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] struct Flag(NonZeroUsize); @@ -758,11 +758,12 @@ impl JsString { #[inline] fn inner(&self) -> InnerKind<'_> { // Check the first bit to 1. - match self.inner.is_static() { + if self.inner.is_static() { // Safety: We already checked. - true => InnerKind::Static(unsafe { self.inner.get_static_unchecked() }), + InnerKind::Static(unsafe { self.inner.get_static_unchecked() }) + } else { // Safety: We already checked. - _ => InnerKind::Heap(unsafe { &*self.inner.get_heap_unchecked() }), + InnerKind::Heap(unsafe { &*self.inner.get_heap_unchecked() }) } } From c0bb5096fa2776a66e5682d11ccc4e14dd9a523e Mon Sep 17 00:00:00 2001 From: YXL Date: Sun, 24 Apr 2022 11:33:52 +0800 Subject: [PATCH 07/14] revert effcaa40 Signed-off-by: YXL --- boa_engine/src/value/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/boa_engine/src/value/mod.rs b/boa_engine/src/value/mod.rs index 719a6689504..2fd12402887 100644 --- a/boa_engine/src/value/mod.rs +++ b/boa_engine/src/value/mod.rs @@ -278,6 +278,7 @@ impl JsValue { /// [spec]: https://tc39.es/ecma262/#sec-toboolean pub fn to_boolean(&self) -> bool { match *self { + Self::Undefined | Self::Null => false, Self::Symbol(_) | Self::Object(_) => true, Self::String(ref s) if !s.is_empty() => true, Self::Rational(n) if n != 0.0 && !n.is_nan() => true, From 34494a1bd9138c82a4032be1459576ebc6ee40f2 Mon Sep 17 00:00:00 2001 From: YXL Date: Sun, 24 Apr 2022 20:37:50 +0800 Subject: [PATCH 08/14] Fix clippy and add some tests Signed-off-by: YXL --- boa_engine/src/string.rs | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/boa_engine/src/string.rs b/boa_engine/src/string.rs index 30a0cfccd4b..7f96418bc66 100644 --- a/boa_engine/src/string.rs +++ b/boa_engine/src/string.rs @@ -625,7 +625,7 @@ pub struct JsString { /// Use the first bit as the flag. /// Detail: #[repr(transparent)] -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] struct Flag(NonZeroUsize); impl Flag { @@ -645,7 +645,7 @@ impl Flag { /// Check if the first bit is 1. #[inline] - const fn is_static(&self) -> bool { + const fn is_static(self) -> bool { self.0.get() & 1 == 1 } @@ -653,7 +653,7 @@ impl Flag { /// /// It maybe a static string. #[inline] - const unsafe fn get_heap_unchecked(&self) -> *mut Inner { + const unsafe fn get_heap_unchecked(self) -> *mut Inner { self.0.get() as *mut _ } @@ -661,7 +661,7 @@ impl Flag { /// /// It maybe a string allocated on the heap. #[inline] - const unsafe fn get_static_unchecked(&self) -> &'static str { + const unsafe fn get_static_unchecked(self) -> &'static str { // shift right to get the index. CONSTANTS_ARRAY[self.0.get() >> 1] } @@ -1093,6 +1093,21 @@ mod tests { assert_eq!(JsString::refcount(&x), 1); } + #[test] + fn static_refcount() { + let x = JsString::new(""); + assert_eq!(JsString::refcount(&x), 0); + + let idx = { + let y = x.clone(); + assert_eq!(JsString::refcount(&x), 0); + assert_eq!(JsString::refcount(&y), 0); + y.inner + }; + + assert_eq!(x.inner, idx); + } + #[test] fn ptr_eq() { let x = JsString::new("Hello"); @@ -1105,6 +1120,18 @@ mod tests { assert!(!JsString::ptr_eq(&y, &z)); } + #[test] + fn static_ptr_eq() { + let x = JsString::new(""); + let y = x.clone(); + + assert!(JsString::ptr_eq(&x, &y)); + + let z = JsString::new(""); + assert!(JsString::ptr_eq(&x, &z)); + assert!(JsString::ptr_eq(&y, &z)); + } + #[test] fn as_str() { let s = "Hello"; From 31feb149ab5f3a9a924ef01b66cc85cc77ddee61 Mon Sep 17 00:00:00 2001 From: YXL Date: Mon, 25 Apr 2022 09:22:17 +0800 Subject: [PATCH 09/14] Use `get_unchecked` Signed-off-by: YXL --- boa_engine/src/string.rs | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/boa_engine/src/string.rs b/boa_engine/src/string.rs index 7f96418bc66..8f39e85e9b9 100644 --- a/boa_engine/src/string.rs +++ b/boa_engine/src/string.rs @@ -615,15 +615,24 @@ impl Inner { /// 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. +/// +/// We define some commonly used string constants in the data segment. For these +/// strings, we no longer allocate memory on the heap to reduce the overhead of +/// memory allocation and reference counting. #[derive(Finalize)] pub struct JsString { inner: Flag, _marker: PhantomData>, } -/// It maybe an index of [`CONSTANTS_ARRAY`], or a raw pointer of [`Inner`]. -/// Use the first bit as the flag. -/// Detail: +/// It may be an index of [`CONSTANTS_ARRAY`], or a raw pointer of [`Inner`]. Use +/// the first bit as the flag (Detail: ). +/// +/// When the first bit is 0, it represents the address of an [`Inner`]. When the +/// first bit is 1, it represents an index of [`CONSTANTS_ARRAY`], and the index +/// number is stored in higher bits. +/// +/// It uses `NonZeroUsize`, which can get benefit from non-null optimization. #[repr(transparent)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] struct Flag(NonZeroUsize); @@ -649,6 +658,9 @@ impl Flag { self.0.get() & 1 == 1 } + /// Returns a reference to a string stroed on the heap, without doing + /// flag checking. + /// /// # Safety /// /// It maybe a static string. @@ -657,13 +669,15 @@ impl Flag { self.0.get() as *mut _ } + /// Returns a reference to a static string, without doing flag checking. + /// /// # Safety /// - /// It maybe a string allocated on the heap. + /// It maybe a string stroed on the heap. #[inline] - const unsafe fn get_static_unchecked(self) -> &'static str { + unsafe fn get_static_unchecked(self) -> &'static str { // shift right to get the index. - CONSTANTS_ARRAY[self.0.get() >> 1] + CONSTANTS_ARRAY.get_unchecked(self.0.get() >> 1) } } @@ -1098,14 +1112,13 @@ mod tests { let x = JsString::new(""); assert_eq!(JsString::refcount(&x), 0); - let idx = { + { let y = x.clone(); assert_eq!(JsString::refcount(&x), 0); assert_eq!(JsString::refcount(&y), 0); - y.inner }; - assert_eq!(x.inner, idx); + assert_eq!(JsString::refcount(&x), 0); } #[test] From 46f7f8b1d01937d94d66c37c996b986d9bdc28da Mon Sep 17 00:00:00 2001 From: YXL Date: Tue, 26 Apr 2022 16:38:56 +0800 Subject: [PATCH 10/14] Fix `miri` error Signed-off-by: YXL --- boa_engine/src/string.rs | 106 ++++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 52 deletions(-) diff --git a/boa_engine/src/string.rs b/boa_engine/src/string.rs index 8f39e85e9b9..a883f3027d6 100644 --- a/boa_engine/src/string.rs +++ b/boa_engine/src/string.rs @@ -8,13 +8,12 @@ use std::{ hash::BuildHasherDefault, hash::{Hash, Hasher}, marker::PhantomData, - num::NonZeroUsize, ops::Deref, - ptr::copy_nonoverlapping, + ptr::{copy_nonoverlapping, NonNull}, rc::Rc, }; -const CONSTANTS_ARRAY: [&str; 426] = [ +const CONSTANTS_ARRAY: [&str; 419] = [ // Empty string "", // Misc @@ -57,14 +56,8 @@ const CONSTANTS_ARRAY: [&str; 426] = [ "isSealed", "freeze", "isFrozen", - "preventExtensions", "isExtensible", - "getOwnPropertyDescriptor", - "getOwnPropertyDescriptors", - "getOwnPropertyNames", - "getOwnPropertySymbols", "hasOwnProperty", - "propertyIsEnumerable", "isPrototypeOf", "setPrototypeOf", "getPrototypeOf", @@ -85,14 +78,12 @@ const CONSTANTS_ARRAY: [&str; 426] = [ "call", // Generator object "Generator", - "GeneratorFunction", // Array object "Array", "at", "from", "isArray", "of", - "get [Symbol.species]", "copyWithin", "entries", "every", @@ -168,8 +159,6 @@ const CONSTANTS_ARRAY: [&str; 426] = [ "MIN_SAFE_INTEGER", "MAX_VALUE", "MIN_VALUE", - "NEGATIVE_INFINITY", - "POSITIVE_INFINITY", "isSafeInteger", "isInteger", "toExponential", @@ -195,29 +184,30 @@ const CONSTANTS_ARRAY: [&str; 426] = [ "unicode", "sticky", "source", + "get hasIndices", + "get global", + "get ignoreCase", + "get multiline", + "get dotAll", + "get unicode", + "get sticky", + "get flags", + "get source", // Symbol object "Symbol", "for", "keyFor", "description", "asyncIterator", - "Symbol.asyncIterator", "hasInstance", - "Symbol.hasInstance", - "isConcatSpreadable", - "Symbol.isConcatSpreadable", "species", "Symbol.species", "unscopables", - "Symbol.unscopables", - "[Symbol.hasInstance]", "iterator", "Symbol.iterator", - "[Symbol.iterator]", "Symbol.match", "[Symbol.match]", "Symbol.matchAll", - "[Symbol.matchAll]", "Symbol.replace", "[Symbol.replace]", "Symbol.search", @@ -225,11 +215,8 @@ const CONSTANTS_ARRAY: [&str; 426] = [ "Symbol.split", "[Symbol.split]", "toStringTag", - "Symbol.toStringTag", - "[Symbol.toStringTag]", "toPrimitive", - "Symbol.toPrimitive", - "[Symbol.toPrimitive]", + "get description", // Map object "Map", "clear", @@ -254,6 +241,7 @@ const CONSTANTS_ARRAY: [&str; 426] = [ "SyntaxError", "ReferenceError", "EvalError", + "ThrowTypeError", "URIError", "message", // Date object @@ -269,12 +257,10 @@ const CONSTANTS_ARRAY: [&str; 426] = [ "getSeconds", "getTime", "getYear", - "getTimezoneOffset", "getUTCDate", "getUTCDay", "getUTCFullYear", "getUTCHours", - "getUTCMilliseconds", "getUTCMinutes", "getUTCMonth", "getUTCSeconds", @@ -290,7 +276,6 @@ const CONSTANTS_ARRAY: [&str; 426] = [ "setUTCDate", "setUTCFullYear", "setUTCHours", - "setUTCMilliseconds", "setUTCMinutes", "setUTCMonth", "setUTCSeconds", @@ -305,6 +290,12 @@ const CONSTANTS_ARRAY: [&str; 426] = [ "JSON", "parse", "stringify", + // Iterator object + "Array Iterator", + "Set Iterator", + "String Iterator", + "Map Iterator", + "For In Iterator", // Math object "Math", "LN10", @@ -352,13 +343,11 @@ const CONSTANTS_ARRAY: [&str; 426] = [ // Intl object "Intl", "DateTimeFormat", - "getCanonicalLocales", // TypedArray object "TypedArray", "ArrayBuffer", "Int8Array", "Uint8Array", - "Uint8ClampedArray", "Int16Array", "Uint16Array", "Int32Array", @@ -367,12 +356,16 @@ const CONSTANTS_ARRAY: [&str; 426] = [ "BigUint64Array", "Float32Array", "Float64Array", - "BYTES_PER_ELEMENT", "buffer", "byteLength", "byteOffset", "isView", "subarray", + "get byteLength", + "get buffer", + "get byteOffset", + "get size", + "get length", // DataView object "DataView", "getBigInt64", @@ -481,6 +474,10 @@ const MAX_CONSTANT_STRING_LENGTH: usize = { } i += 1; } + // The FX algorithm is in hashing 8 bytes at a time on 64-bit platforms. + // So set the maximum number to be a multiple of 8, 16 is not a proven + // value and may change in the future. + assert!(max <= 16); max }; @@ -524,7 +521,7 @@ struct Inner { impl Inner { /// Create a new `Inner` from `&str`. #[inline] - fn new(s: &str) -> *mut Self { + fn new(s: &str) -> NonNull { // We get the layout of the `Inner` type and we extend by the size // of the string array. let inner_layout = Layout::new::(); @@ -532,7 +529,7 @@ impl Inner { .extend(Layout::array::(s.len()).expect("failed to create memory layout")) .expect("failed to extend memory layout"); - unsafe { + let inner = unsafe { let inner = try_alloc(layout).cast::(); // Write the first part, the Inner. @@ -551,12 +548,15 @@ impl Inner { 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) } } /// Concatenate array of strings. #[inline] - fn concat_array(strings: &[&str]) -> *mut Self { + fn concat_array(strings: &[&str]) -> NonNull { let mut total_string_size = 0; for string in strings { total_string_size += string.len(); @@ -569,7 +569,7 @@ impl Inner { .extend(Layout::array::(total_string_size).expect("failed to create memory layout")) .expect("failed to extend memory layout"); - unsafe { + let inner = unsafe { let inner = try_alloc(layout).cast::(); // Write the first part, the Inner. @@ -592,20 +592,23 @@ impl Inner { } 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: *mut Self) { - let len = (*x).len; + unsafe fn dealloc(x: NonNull) { + let len = (*x.as_ptr()).len; let inner_layout = Layout::new::(); let (layout, _offset) = inner_layout .extend(Layout::array::(len).expect("failed to create memory layout")) .expect("failed to extend memory layout"); - dealloc(x.cast::<_>(), layout); + dealloc(x.as_ptr().cast::<_>(), layout); } } @@ -632,16 +635,15 @@ pub struct JsString { /// first bit is 1, it represents an index of [`CONSTANTS_ARRAY`], and the index /// number is stored in higher bits. /// -/// It uses `NonZeroUsize`, which can get benefit from non-null optimization. +/// It uses `NonNull`, which can get benefit from non-null optimization. #[repr(transparent)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] -struct Flag(NonZeroUsize); +struct Flag(NonNull); impl Flag { #[inline] - fn new_heap(inner: *mut Inner) -> Self { - // Safety: We already know it's not null, so this is safe. - Self(unsafe { NonZeroUsize::new_unchecked(inner as usize) }) + fn new_heap(inner: NonNull) -> Self { + Self(inner) } /// Set the first bit to 1, indicating that it is static. Store the index @@ -649,13 +651,13 @@ impl Flag { #[inline] const fn new_static(idx: usize) -> Self { // Safety: We already know it's not null, so this is safe. - Self(unsafe { NonZeroUsize::new_unchecked((idx << 1) | 1) }) + Self(unsafe { NonNull::new_unchecked(((idx << 1) | 1) as *mut _) }) } /// Check if the first bit is 1. #[inline] - const fn is_static(self) -> bool { - self.0.get() & 1 == 1 + fn is_static(self) -> bool { + (self.0.as_ptr() as usize) & 1 == 1 } /// Returns a reference to a string stroed on the heap, without doing @@ -665,8 +667,8 @@ impl Flag { /// /// It maybe a static string. #[inline] - const unsafe fn get_heap_unchecked(self) -> *mut Inner { - self.0.get() as *mut _ + const unsafe fn get_heap_unchecked(self) -> NonNull { + self.0 } /// Returns a reference to a static string, without doing flag checking. @@ -677,7 +679,7 @@ impl Flag { #[inline] unsafe fn get_static_unchecked(self) -> &'static str { // shift right to get the index. - CONSTANTS_ARRAY.get_unchecked(self.0.get() >> 1) + CONSTANTS_ARRAY.get_unchecked((self.0.as_ptr() as usize) >> 1) } } @@ -777,7 +779,7 @@ impl JsString { InnerKind::Static(unsafe { self.inner.get_static_unchecked() }) } else { // Safety: We already checked. - InnerKind::Heap(unsafe { &*self.inner.get_heap_unchecked() }) + InnerKind::Heap(unsafe { self.inner.get_heap_unchecked().as_ref() }) } } From 29fadb2d5d7051203fde030e6c054182f0aa084e Mon Sep 17 00:00:00 2001 From: YXL Date: Wed, 27 Apr 2022 10:15:26 +0800 Subject: [PATCH 11/14] Clearer documentation Signed-off-by: YXL --- boa_engine/src/string.rs | 76 +++++++++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 29 deletions(-) diff --git a/boa_engine/src/string.rs b/boa_engine/src/string.rs index a883f3027d6..75eb8979351 100644 --- a/boa_engine/src/string.rs +++ b/boa_engine/src/string.rs @@ -474,10 +474,6 @@ const MAX_CONSTANT_STRING_LENGTH: usize = { } i += 1; } - // The FX algorithm is in hashing 8 bytes at a time on 64-bit platforms. - // So set the maximum number to be a multiple of 8, 16 is not a proven - // value and may change in the future. - assert!(max <= 16); max }; @@ -495,7 +491,8 @@ thread_local! { let mut constants = FxHashSet::with_capacity_and_hasher(len, BuildHasherDefault::::default()); for idx in 0..len { - let s = JsString::new_static(idx); + // Safety: We already know it's an index of [`CONSTANTS_ARRAY`]. + let s = unsafe { JsString::new_static(idx) }; constants.insert(s); } @@ -619,7 +616,7 @@ impl Inner { /// 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. /// -/// We define some commonly used string constants in the data segment. For these +/// We define some commonly used string constants in [`CONSTANTS_ARRAY`]. For these /// strings, we no longer allocate memory on the heap to reduce the overhead of /// memory allocation and reference counting. #[derive(Finalize)] @@ -628,54 +625,70 @@ pub struct JsString { _marker: PhantomData>, } -/// It may be an index of [`CONSTANTS_ARRAY`], or a raw pointer of [`Inner`]. Use -/// the first bit as the flag (Detail: ). +/// This struct uses a technique called tagged pointer to benefit from the fact that newly +/// allocated pointers are always word aligned on 64-bits platforms, making it impossible +/// to have a LSB equal to 1. More details about this technique on the article of Wikipedia +/// about [tagged pointers][tagged_wp]. /// -/// When the first bit is 0, it represents the address of an [`Inner`]. When the -/// first bit is 1, it represents an index of [`CONSTANTS_ARRAY`], and the index -/// number is stored in higher bits. +/// # Representation /// -/// It uses `NonNull`, which can get benefit from non-null optimization. +/// If the LSB of the internal [`NonNull`] is set (1), then the pointer address represents +/// an index value for [`CONSTANTS_ARRAY`], where the remaining MSBs store the index. +/// Otherwise, the whole pointer represents the address of a heap allocated [`Inner`]. +/// +/// It uses [`NonNull`], which guarantees that `Flag` (and subsequently [`JsString`]) +/// can use the "null pointer optimization" to optimize the size of [`Option`]. +/// +/// # Provenance +/// +/// This struct stores a [`NonNull`] instead of a [`NonZeroUsize`][std::num::NonZeroUsize] +/// in order to preserve the provenance of our valid heap pointers. +/// On the other hand, all index values are just casted to invalid pointers, +/// because we don't need to preserve the provenance of [`usize`] indices. +/// +/// [tagged_wp]: https://en.wikipedia.org/wiki/Tagged_pointer #[repr(transparent)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] struct Flag(NonNull); impl Flag { #[inline] - fn new_heap(inner: NonNull) -> Self { + unsafe fn new_heap(inner: NonNull) -> Self { Self(inner) } - /// Set the first bit to 1, indicating that it is static. Store the index - /// in 1..63 bits. + /// Create a new static `Flag` from the index of an element inside [`CONSTANTS_ARRAY`]. #[inline] - const fn new_static(idx: usize) -> Self { + const unsafe fn new_static(idx: usize) -> Self { // Safety: We already know it's not null, so this is safe. - Self(unsafe { NonNull::new_unchecked(((idx << 1) | 1) as *mut _) }) + Self(NonNull::new_unchecked(((idx << 1) | 1) as *mut _)) } - /// Check if the first bit is 1. + /// Check if `Flag` contains an index for [`CONSTANTS_ARRAY`]. #[inline] fn is_static(self) -> bool { (self.0.as_ptr() as usize) & 1 == 1 } - /// Returns a reference to a string stroed on the heap, without doing - /// flag checking. + /// Returns a reference to a string stored on the heap, + /// without checking if its internal pointer is valid. /// /// # Safety /// - /// It maybe a static string. + /// Calling this method with a static `Flag` results in Undefined Behaviour. #[inline] const unsafe fn get_heap_unchecked(self) -> NonNull { self.0 } - /// Returns a reference to a static string, without doing flag checking. + /// Returns the string inside [`CONSTANTS_ARRAY`] corresponding to the + /// index inside `Flag`, without checking its validity. /// /// # Safety /// - /// It maybe a string stroed on the heap. + /// Calling this method with a `Flag` storing an out of bounds index for + /// [`CONSTANTS_ARRAY`] or a valid pointer to a heap allocated [`Inner`] + /// results in Undefined Behaviour. #[inline] unsafe fn get_static_unchecked(self) -> &'static str { // shift right to get the index. @@ -686,11 +699,13 @@ impl Flag { impl Default for JsString { #[inline] fn default() -> Self { - Self::new_static(0) + // Safety: We already know it's an index of [`CONSTANTS_ARRAY`]. + unsafe { Self::new_static(0) } } } -/// Data stored in [`JsString`]. +/// Enum representing either a reference to a heap allocated [`Inner`] +/// or a static reference to a [`str`] inside [`CONSTANTS_ARRAY`]. enum InnerKind<'a> { // A string allocated on the heap. Heap(&'a Inner), @@ -701,7 +716,7 @@ enum InnerKind<'a> { impl JsString { /// Create a new JavaScript string from an index of [`CONSTANTS_ARRAY`]. #[inline] - fn new_static(idx: usize) -> Self { + unsafe fn new_static(idx: usize) -> Self { Self { inner: Flag::new_static(idx), _marker: PhantomData, @@ -726,7 +741,8 @@ impl JsString { } Self { - inner: Flag::new_heap(Inner::new(s)), + // Safety: We already know it's a valid heap pointer. + inner: unsafe { Flag::new_heap(Inner::new(s)) }, _marker: PhantomData, } } @@ -741,7 +757,8 @@ impl JsString { let y = y.as_ref(); let this = Self { - inner: Flag::new_heap(Inner::concat_array(&[x, y])), + // Safety: We already know it's a valid heap pointer. + inner: unsafe { Flag::new_heap(Inner::concat_array(&[x, y])) }, _marker: PhantomData, }; @@ -757,7 +774,8 @@ impl JsString { /// Concatenate array of string. pub fn concat_array(strings: &[&str]) -> Self { let this = Self { - inner: Flag::new_heap(Inner::concat_array(strings)), + // Safety: We already know it's a valid heap pointer. + inner: unsafe { Flag::new_heap(Inner::concat_array(strings)) }, _marker: PhantomData, }; From 9349a0874d409f7c394a4e719614b6a202987994 Mon Sep 17 00:00:00 2001 From: YXL Date: Wed, 27 Apr 2022 11:45:29 +0800 Subject: [PATCH 12/14] Change `CONSTANTS` Signed-off-by: YXL --- boa_engine/src/string.rs | 65 +++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/boa_engine/src/string.rs b/boa_engine/src/string.rs index 75eb8979351..79ebdaee12e 100644 --- a/boa_engine/src/string.rs +++ b/boa_engine/src/string.rs @@ -1,6 +1,6 @@ use crate::builtins::string::is_trimmable_whitespace; use boa_gc::{unsafe_empty_trace, Finalize, Trace}; -use rustc_hash::{FxHashSet, FxHasher}; +use rustc_hash::{FxHashMap, FxHasher}; use std::{ alloc::{alloc, dealloc, handle_alloc_error, Layout}, borrow::Borrow, @@ -486,14 +486,16 @@ unsafe fn try_alloc(layout: Layout) -> *mut u8 { } thread_local! { - static CONSTANTS: FxHashSet = { - let len = CONSTANTS_ARRAY.len(); - let mut constants = FxHashSet::with_capacity_and_hasher(len, BuildHasherDefault::::default()); + static CONSTANTS: FxHashMap<&'static str, JsString> = { + let mut constants = FxHashMap::with_capacity_and_hasher( + CONSTANTS_ARRAY.len(), + BuildHasherDefault::::default(), + ); - for idx in 0..len { + for (idx, &s) in CONSTANTS_ARRAY.iter().enumerate() { // Safety: We already know it's an index of [`CONSTANTS_ARRAY`]. - let s = unsafe { JsString::new_static(idx) }; - constants.insert(s); + let v = unsafe { JsString::new_static(idx) }; + constants.insert(s, v); } constants @@ -607,6 +609,14 @@ impl Inner { dealloc(x.as_ptr().cast::<_>(), layout); } + + #[inline] + fn as_str(&self) -> &str { + unsafe { + let slice = std::slice::from_raw_parts(self.data.as_ptr(), self.len); + std::str::from_utf8_unchecked(slice) + } + } } /// This represents a JavaScript primitive string. @@ -756,36 +766,40 @@ impl JsString { let x = x.as_ref(); let y = y.as_ref(); - let this = Self { - // Safety: We already know it's a valid heap pointer. - inner: unsafe { Flag::new_heap(Inner::concat_array(&[x, y])) }, - _marker: PhantomData, - }; + let inner = Inner::concat_array(&[x, y]); - if this.len() <= MAX_CONSTANT_STRING_LENGTH { - if let Some(constant) = CONSTANTS.with(|c| c.get(&this).cloned()) { + if unsafe { inner.as_ref() }.len <= MAX_CONSTANT_STRING_LENGTH { + if let Some(constant) = + CONSTANTS.with(|c| c.get(unsafe { inner.as_ref() }.as_str()).cloned()) + { return constant; } } - this + Self { + // Safety: We already know it's a valid heap pointer. + inner: unsafe { Flag::new_heap(inner) }, + _marker: PhantomData, + } } /// Concatenate array of string. pub fn concat_array(strings: &[&str]) -> Self { - let this = Self { - // Safety: We already know it's a valid heap pointer. - inner: unsafe { Flag::new_heap(Inner::concat_array(strings)) }, - _marker: PhantomData, - }; + let inner = Inner::concat_array(strings); - if this.len() <= MAX_CONSTANT_STRING_LENGTH { - if let Some(constant) = CONSTANTS.with(|c| c.get(&this).cloned()) { + if unsafe { inner.as_ref() }.len <= MAX_CONSTANT_STRING_LENGTH { + if let Some(constant) = + CONSTANTS.with(|c| c.get(unsafe { inner.as_ref() }.as_str()).cloned()) + { return constant; } } - this + Self { + // Safety: We already know it's a valid heap pointer. + inner: unsafe { Flag::new_heap(inner) }, + _marker: PhantomData, + } } /// Return the inner representation. @@ -805,10 +819,7 @@ impl JsString { #[inline] pub fn as_str(&self) -> &str { match self.inner() { - InnerKind::Heap(inner) => unsafe { - let slice = std::slice::from_raw_parts(inner.data.as_ptr(), inner.len); - std::str::from_utf8_unchecked(slice) - }, + InnerKind::Heap(inner) => inner.as_str(), InnerKind::Static(inner) => inner, } } From 456cd19cf2e02062d96adfbb5efa3be8a080c765 Mon Sep 17 00:00:00 2001 From: YXL Date: Wed, 27 Apr 2022 13:15:41 +0800 Subject: [PATCH 13/14] Better type Signed-off-by: YXL --- boa_engine/src/string.rs | 69 ++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/boa_engine/src/string.rs b/boa_engine/src/string.rs index 79ebdaee12e..97a428a8e8d 100644 --- a/boa_engine/src/string.rs +++ b/boa_engine/src/string.rs @@ -631,7 +631,7 @@ impl Inner { /// memory allocation and reference counting. #[derive(Finalize)] pub struct JsString { - inner: Flag, + inner: TaggedInner, _marker: PhantomData>, } @@ -646,8 +646,8 @@ pub struct JsString { /// an index value for [`CONSTANTS_ARRAY`], where the remaining MSBs store the index. /// Otherwise, the whole pointer represents the address of a heap allocated [`Inner`]. /// -/// It uses [`NonNull`], which guarantees that `Flag` (and subsequently [`JsString`]) -/// can use the "null pointer optimization" to optimize the size of [`Option`]. +/// It uses [`NonNull`], which guarantees that `TaggedInner` (and subsequently [`JsString`]) +/// can use the "null pointer optimization" to optimize the size of [`Option`]. /// /// # Provenance /// @@ -659,22 +659,23 @@ pub struct JsString { /// [tagged_wp]: https://en.wikipedia.org/wiki/Tagged_pointer #[repr(transparent)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] -struct Flag(NonNull); +struct TaggedInner(NonNull); -impl Flag { +impl TaggedInner { #[inline] unsafe fn new_heap(inner: NonNull) -> Self { Self(inner) } - /// Create a new static `Flag` from the index of an element inside [`CONSTANTS_ARRAY`]. + /// Create a new static `TaggedInner` from the index of an element inside + /// [`CONSTANTS_ARRAY`]. #[inline] const unsafe fn new_static(idx: usize) -> Self { // Safety: We already know it's not null, so this is safe. Self(NonNull::new_unchecked(((idx << 1) | 1) as *mut _)) } - /// Check if `Flag` contains an index for [`CONSTANTS_ARRAY`]. + /// Check if `TaggedInner` contains an index for [`CONSTANTS_ARRAY`]. #[inline] fn is_static(self) -> bool { (self.0.as_ptr() as usize) & 1 == 1 @@ -685,19 +686,19 @@ impl Flag { /// /// # Safety /// - /// Calling this method with a static `Flag` results in Undefined Behaviour. + /// Calling this method with a static `TaggedInner` results in Undefined Behaviour. #[inline] const unsafe fn get_heap_unchecked(self) -> NonNull { self.0 } /// Returns the string inside [`CONSTANTS_ARRAY`] corresponding to the - /// index inside `Flag`, without checking its validity. + /// index inside `TaggedInner`, without checking its validity. /// /// # Safety /// - /// Calling this method with a `Flag` storing an out of bounds index for - /// [`CONSTANTS_ARRAY`] or a valid pointer to a heap allocated [`Inner`] + /// Calling this method with a `TaggedInner` storing an out of bounds index + /// for [`CONSTANTS_ARRAY`] or a valid pointer to a heap allocated [`Inner`] /// results in Undefined Behaviour. #[inline] unsafe fn get_static_unchecked(self) -> &'static str { @@ -728,7 +729,7 @@ impl JsString { #[inline] unsafe fn new_static(idx: usize) -> Self { Self { - inner: Flag::new_static(idx), + inner: TaggedInner::new_static(idx), _marker: PhantomData, } } @@ -752,7 +753,7 @@ impl JsString { Self { // Safety: We already know it's a valid heap pointer. - inner: unsafe { Flag::new_heap(Inner::new(s)) }, + inner: unsafe { TaggedInner::new_heap(Inner::new(s)) }, _marker: PhantomData, } } @@ -778,7 +779,7 @@ impl JsString { Self { // Safety: We already know it's a valid heap pointer. - inner: unsafe { Flag::new_heap(inner) }, + inner: unsafe { TaggedInner::new_heap(inner) }, _marker: PhantomData, } } @@ -797,7 +798,7 @@ impl JsString { Self { // Safety: We already know it's a valid heap pointer. - inner: unsafe { Flag::new_heap(inner) }, + inner: unsafe { TaggedInner::new_heap(inner) }, _marker: PhantomData, } } @@ -826,10 +827,10 @@ impl JsString { /// Gets the number of `JsString`s which point to this allocation. #[inline] - pub fn refcount(this: &Self) -> usize { + pub fn refcount(this: &Self) -> Option { match this.inner() { - InnerKind::Heap(inner) => inner.refcount.get(), - InnerKind::Static(_inner) => 0, + InnerKind::Heap(inner) => Some(inner.refcount.get()), + InnerKind::Static(_inner) => None, } } @@ -1117,39 +1118,39 @@ mod tests { #[test] fn refcount() { let x = JsString::new("Hello wrold"); - assert_eq!(JsString::refcount(&x), 1); + assert_eq!(JsString::refcount(&x), Some(1)); { let y = x.clone(); - assert_eq!(JsString::refcount(&x), 2); - assert_eq!(JsString::refcount(&y), 2); + assert_eq!(JsString::refcount(&x), Some(2)); + assert_eq!(JsString::refcount(&y), Some(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), Some(3)); + assert_eq!(JsString::refcount(&y), Some(3)); + assert_eq!(JsString::refcount(&z), Some(3)); } - assert_eq!(JsString::refcount(&x), 2); - assert_eq!(JsString::refcount(&y), 2); + assert_eq!(JsString::refcount(&x), Some(2)); + assert_eq!(JsString::refcount(&y), Some(2)); } - assert_eq!(JsString::refcount(&x), 1); + assert_eq!(JsString::refcount(&x), Some(1)); } #[test] fn static_refcount() { let x = JsString::new(""); - assert_eq!(JsString::refcount(&x), 0); + assert_eq!(JsString::refcount(&x), None); { let y = x.clone(); - assert_eq!(JsString::refcount(&x), 0); - assert_eq!(JsString::refcount(&y), 0); + assert_eq!(JsString::refcount(&x), None); + assert_eq!(JsString::refcount(&y), None); }; - assert_eq!(JsString::refcount(&x), 0); + assert_eq!(JsString::refcount(&x), None); } #[test] @@ -1213,14 +1214,14 @@ mod tests { let xy = JsString::concat(x, y); assert_eq!(xy, "hello, "); - assert_eq!(JsString::refcount(&xy), 1); + assert_eq!(JsString::refcount(&xy), Some(1)); let xyz = JsString::concat(xy, z); assert_eq!(xyz, "hello, world"); - assert_eq!(JsString::refcount(&xyz), 1); + assert_eq!(JsString::refcount(&xyz), Some(1)); let xyzw = JsString::concat(xyz, w); assert_eq!(xyzw, "hello, world!"); - assert_eq!(JsString::refcount(&xyzw), 1); + assert_eq!(JsString::refcount(&xyzw), Some(1)); } } From 698a517cd5f913774f1241dc6ccddbdc005488e2 Mon Sep 17 00:00:00 2001 From: YXL Date: Fri, 29 Apr 2022 09:36:29 +0800 Subject: [PATCH 14/14] Fix memory leak Signed-off-by: YXL --- boa_engine/src/string.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/boa_engine/src/string.rs b/boa_engine/src/string.rs index 97a428a8e8d..7b4eb837220 100644 --- a/boa_engine/src/string.rs +++ b/boa_engine/src/string.rs @@ -768,11 +768,11 @@ impl JsString { let y = y.as_ref(); let inner = Inner::concat_array(&[x, y]); + let s = unsafe { inner.as_ref() }.as_str(); - if unsafe { inner.as_ref() }.len <= MAX_CONSTANT_STRING_LENGTH { - if let Some(constant) = - CONSTANTS.with(|c| c.get(unsafe { inner.as_ref() }.as_str()).cloned()) - { + if s.len() <= MAX_CONSTANT_STRING_LENGTH { + if let Some(constant) = CONSTANTS.with(|c| c.get(s).cloned()) { + unsafe { Inner::dealloc(inner) }; return constant; } } @@ -787,11 +787,11 @@ impl JsString { /// Concatenate array of string. pub fn concat_array(strings: &[&str]) -> Self { let inner = Inner::concat_array(strings); + let s = unsafe { inner.as_ref() }.as_str(); - if unsafe { inner.as_ref() }.len <= MAX_CONSTANT_STRING_LENGTH { - if let Some(constant) = - CONSTANTS.with(|c| c.get(unsafe { inner.as_ref() }.as_str()).cloned()) - { + if s.len() <= MAX_CONSTANT_STRING_LENGTH { + if let Some(constant) = CONSTANTS.with(|c| c.get(s).cloned()) { + unsafe { Inner::dealloc(inner) }; return constant; } }