diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index 4095262ebe..d2e4ff039b 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -1127,21 +1128,30 @@ Value ArrayExpression::execute(Interpreter& interpreter) const return {}; if (element->is_spread_expression()) { - if (!value.is_array()) { - interpreter.throw_exception(String::format("%s is not iterable", value.to_string().characters())); - return {}; - } - - auto& array_to_spread = static_cast(value.as_object()); - for (auto& it : array_to_spread.elements()) { - if (it.is_empty()) { - array->elements().append(js_undefined()); - } else { - array->elements().append(it); + // FIXME: Support arbitrary iterables + if (value.is_array()) { + auto& array_to_spread = static_cast(value.as_object()); + for (auto& it : array_to_spread.elements()) { + if (it.is_empty()) { + array->elements().append(js_undefined()); + } else { + array->elements().append(it); + } } + continue; } - - continue; + if (value.is_string() || (value.is_object() && value.as_object().is_string_object())) { + String string_to_spread; + if (value.is_string()) + string_to_spread = value.as_string()->string(); + else + string_to_spread = static_cast(&value.as_object())->primitive_string()->string(); + for (size_t i = 0; i < string_to_spread.length(); ++i) + array->elements().append(js_string(interpreter, string_to_spread.substring(i, 1))); + continue; + } + interpreter.throw_exception(String::format("%s is not iterable", value.to_string().characters())); + return {}; } } array->elements().append(value); diff --git a/Libraries/LibJS/Tests/array-spread.js b/Libraries/LibJS/Tests/array-spread.js index 470b8cb09d..1132f0cc07 100644 --- a/Libraries/LibJS/Tests/array-spread.js +++ b/Libraries/LibJS/Tests/array-spread.js @@ -24,17 +24,17 @@ try { assert(testArray(arr)); assertThrowsError(() => { - [...1]; + [...1]; }, { - error: TypeError, - message: "1 is not iterable", + error: TypeError, + message: "1 is not iterable", }); assertThrowsError(() => { - [...{}]; + [...{}]; }, { - error: TypeError, - message: "[object Object] is not iterable", + error: TypeError, + message: "[object Object] is not iterable", }); console.log("PASS"); diff --git a/Libraries/LibJS/Tests/string-spread.js b/Libraries/LibJS/Tests/string-spread.js new file mode 100644 index 0000000000..433aa3b066 --- /dev/null +++ b/Libraries/LibJS/Tests/string-spread.js @@ -0,0 +1,31 @@ +load("test-common.js"); + +function testArray(arr) { + return arr.length === 4 && + arr[0] === "a" && + arr[1] === "b" && + arr[2] === "c" && + arr[3] === "d"; +} + +try { + var arr; + + arr = ["a", ..."bc", "d"]; + assert(testArray(arr)); + + let s = "bc"; + arr = ["a", ...s, "d"]; + assert(testArray(arr)); + + let obj = { a: "bc" }; + arr = ["a", ...obj.a, "d"]; + assert(testArray(arr)); + + arr = [..."", ...[...new String("abc")], "d"]; + assert(testArray(arr)); + + console.log("PASS"); +} catch (e) { + console.log("FAIL: " + e); +}