From ab652fa1eec8f432fbbe7cd3ffe405f881dc802c Mon Sep 17 00:00:00 2001 From: Matthew Olsson Date: Wed, 6 May 2020 17:49:48 -0700 Subject: [PATCH] LibJS: Add String.raw --- Libraries/LibJS/Runtime/StringConstructor.cpp | 31 +++++++++++++++- Libraries/LibJS/Runtime/StringConstructor.h | 2 ++ Libraries/LibJS/Tests/String.raw.js | 35 +++++++++++++++++++ 3 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 Libraries/LibJS/Tests/String.raw.js diff --git a/Libraries/LibJS/Runtime/StringConstructor.cpp b/Libraries/LibJS/Runtime/StringConstructor.cpp index 0be1df0b24..0a375842c0 100644 --- a/Libraries/LibJS/Runtime/StringConstructor.cpp +++ b/Libraries/LibJS/Runtime/StringConstructor.cpp @@ -24,12 +24,13 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include +#include #include #include #include #include -#include namespace JS { @@ -38,6 +39,8 @@ StringConstructor::StringConstructor() { put("prototype", interpreter().global_object().string_prototype(), 0); put("length", Value(1), Attribute::Configurable); + + put_native_function("raw", raw, 0, Attribute::Writable | Attribute::Configurable); } StringConstructor::~StringConstructor() @@ -63,4 +66,30 @@ Value StringConstructor::construct(Interpreter& interpreter) return StringObject::create(interpreter.global_object(), *primitive_string); } +Value StringConstructor::raw(Interpreter& interpreter) +{ + auto* template_object = interpreter.argument(0).to_object(interpreter.heap()); + if (interpreter.exception()) + return {}; + + auto raw = template_object->get("raw"); + if (raw.is_empty() || raw.is_undefined() || raw.is_null()) { + interpreter.throw_exception(String::format("Cannot convert property 'raw' to object from %s", raw.is_null() ? "null" : "undefined")); + return {}; + } + if (!raw.is_array()) + return js_string(interpreter, ""); + + auto& raw_array_elements = static_cast(raw.to_object(interpreter.heap()))->elements(); + StringBuilder builder; + + for (size_t i = 0; i < raw_array_elements.size(); ++i) { + builder.append(raw_array_elements.at(i).to_string()); + if (i + 1 < interpreter.argument_count() && i < raw_array_elements.size() - 1) + builder.append(interpreter.argument(i + 1).to_string()); + } + + return js_string(interpreter, builder.build()); +} + } diff --git a/Libraries/LibJS/Runtime/StringConstructor.h b/Libraries/LibJS/Runtime/StringConstructor.h index 72bec04aa6..7e33c08910 100644 --- a/Libraries/LibJS/Runtime/StringConstructor.h +++ b/Libraries/LibJS/Runtime/StringConstructor.h @@ -41,6 +41,8 @@ public: private: virtual bool has_constructor() const override { return true; } virtual const char* class_name() const override { return "StringConstructor"; } + + static Value raw(Interpreter&); }; } diff --git a/Libraries/LibJS/Tests/String.raw.js b/Libraries/LibJS/Tests/String.raw.js new file mode 100644 index 0000000000..6a4e172b73 --- /dev/null +++ b/Libraries/LibJS/Tests/String.raw.js @@ -0,0 +1,35 @@ +load("test-common.js") + +try { + let str = String.raw`foo\nbar`; + assert(str.length === 8 && str === "foo\\nbar"); + + str = String.raw`foo ${1 + 9}\nbar${"hf!"}`; + assert(str === "foo 10\\nbarhf!"); + + str = String.raw`${10}${20}${30}`; + assert(str === "102030"); + + str = String.raw({ raw: ["foo ", "\\nbar"] }, 10, "hf!"); + assert(str === "foo 10\\nbar"); + + str = String.raw({ raw: ["foo ", "\\nbar"] }); + assert(str === "foo \\nbar"); + + str = String.raw({ raw: [] }, 10, "hf!"); + assert(str === ""); + + str = String.raw({ raw: 1 }); + assert(str === ""); + + assertThrowsError(() => { + String.raw({}); + }, { + error: TypeError, + message: "Cannot convert property 'raw' to object from undefined", + }); + + console.log("PASS"); +} catch (e) { + console.log("FAIL: " + e); +}