diff --git a/boa/src/builtins/string/mod.rs b/boa/src/builtins/string/mod.rs index ac65d5b7cf..0610f51bb1 100644 --- a/boa/src/builtins/string/mod.rs +++ b/boa/src/builtins/string/mod.rs @@ -189,7 +189,8 @@ impl String { let primitive_val = this.to_string(ctx)?; let pos = args .get(0) - .expect("failed to get argument for String method") + .cloned() + .unwrap_or_else(Value::undefined) .to_integer(ctx)? as i32; // Calling .len() on a string would give the wrong result, as they are bytes not the number of @@ -235,7 +236,8 @@ impl String { let length = primitive_val.chars().count(); let pos = args .get(0) - .expect("failed to get argument for String method") + .cloned() + .unwrap_or_else(Value::undefined) .to_integer(ctx)? as i32; if pos >= length as i32 || pos < 0 { @@ -325,20 +327,21 @@ impl String { // Then we convert it into a Rust String by wrapping it in from_value let primitive_val = this.to_string(ctx)?; + // Calling .len() on a string would give the wrong result, as they are bytes not the number of unicode code points + // Note that this is an O(N) operation (because UTF-8 is complex) while getting the number of bytes is an O(1) operation. + let length = primitive_val.chars().count() as i32; + let start = args .get(0) - .expect("failed to get argument for String method") + .cloned() + .unwrap_or_else(Value::undefined) .to_integer(ctx)? as i32; - let end = args .get(1) - .expect("failed to get argument in slice") + .cloned() + .unwrap_or_else(|| Value::integer(length)) .to_integer(ctx)? as i32; - // Calling .len() on a string would give the wrong result, as they are bytes not the number of unicode code points - // Note that this is an O(N) operation (because UTF-8 is complex) while getting the number of bytes is an O(1) operation. - let length = primitive_val.chars().count() as i32; - let from = if start < 0 { max(length.wrapping_add(start), 0) } else { diff --git a/boa/src/builtins/string/tests.rs b/boa/src/builtins/string/tests.rs index af0eeb0d32..eeb9922013 100644 --- a/boa/src/builtins/string/tests.rs +++ b/boa/src/builtins/string/tests.rs @@ -766,6 +766,34 @@ fn last_index_non_integer_position_argument() { assert_eq!(forward(&mut engine, "'abcx'.lastIndexOf('x', null)"), "3"); } +#[test] +fn char_at() { + let mut engine = Context::new(); + assert_eq!(forward(&mut engine, "'abc'.charAt(1)"), "\"b\""); + assert_eq!(forward(&mut engine, "'abc'.charAt(9)"), "\"\""); + assert_eq!(forward(&mut engine, "'abc'.charAt()"), "\"a\""); + assert_eq!(forward(&mut engine, "'abc'.charAt(null)"), "\"a\""); +} + +#[test] +fn char_code_at() { + let mut engine = Context::new(); + assert_eq!(forward(&mut engine, "'abc'.charCodeAt(1)"), "98"); + assert_eq!(forward(&mut engine, "'abc'.charCodeAt(9)"), "NaN"); + assert_eq!(forward(&mut engine, "'abc'.charCodeAt()"), "97"); + assert_eq!(forward(&mut engine, "'abc'.charCodeAt(null)"), "97"); +} + +#[test] +fn slice() { + let mut engine = Context::new(); + assert_eq!(forward(&mut engine, "'abc'.slice()"), "\"abc\""); + assert_eq!(forward(&mut engine, "'abc'.slice(1)"), "\"bc\""); + assert_eq!(forward(&mut engine, "'abc'.slice(-1)"), "\"c\""); + assert_eq!(forward(&mut engine, "'abc'.slice(0, 9)"), "\"abc\""); + assert_eq!(forward(&mut engine, "'abc'.slice(9, 10)"), "\"\""); +} + #[test] fn empty_iter() { let mut engine = Context::new();