LibJS: Add symbol objects

This commit adds the following classes: SymbolObject, SymbolConstructor,
SymbolPrototype, and Symbol. This commit does not introduce any
new functionality to the Object class, so they cannot be used as
property keys in objects.
This commit is contained in:
mattco98 2020-04-29 23:25:21 -07:00 committed by Andreas Kling
parent b5b08fba92
commit 4ced126704
Notes: sideshowbarker 2024-07-19 06:33:31 +09:00
21 changed files with 819 additions and 3 deletions

View file

@ -491,6 +491,8 @@ Value UnaryExpression::execute(Interpreter& interpreter) const
return js_string(interpreter, "object");
case Value::Type::Boolean:
return js_string(interpreter, "boolean");
case Value::Type::Symbol:
return js_string(interpreter, "symbol");
default:
ASSERT_NOT_REACHED();
}

View file

@ -46,6 +46,10 @@ set(SOURCES
Runtime/StringConstructor.cpp
Runtime/StringObject.cpp
Runtime/StringPrototype.cpp
Runtime/Symbol.cpp
Runtime/SymbolConstructor.cpp
Runtime/SymbolObject.cpp
Runtime/SymbolPrototype.cpp
Runtime/Uint8ClampedArray.cpp
Runtime/Value.cpp
Token.cpp

View file

@ -34,7 +34,8 @@
__JS_ENUMERATE(Function, function, FunctionPrototype, FunctionConstructor) \
__JS_ENUMERATE(NumberObject, number, NumberPrototype, NumberConstructor) \
__JS_ENUMERATE(Object, object, ObjectPrototype, ObjectConstructor) \
__JS_ENUMERATE(StringObject, string, StringPrototype, StringConstructor)
__JS_ENUMERATE(StringObject, string, StringPrototype, StringConstructor) \
__JS_ENUMERATE(SymbolObject, symbol, SymbolPrototype, SymbolConstructor)
#define JS_ENUMERATE_ERROR_SUBCLASSES \
__JS_ENUMERATE(EvalError, eval_error, EvalErrorPrototype, EvalErrorConstructor) \
@ -70,6 +71,7 @@ class Reference;
class ScopeNode;
class Shape;
class Statement;
class Symbol;
class Uint8ClampedArray;
class Value;
enum class DeclarationKind;

View file

@ -36,6 +36,7 @@
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Reference.h>
#include <LibJS/Runtime/Shape.h>
#include <LibJS/Runtime/SymbolObject.h>
#include <LibJS/Runtime/Value.h>
namespace JS {
@ -195,6 +196,8 @@ void Interpreter::gather_roots(Badge<Heap>, HashTable<Cell*>& roots)
}
roots.set(call_frame.environment);
}
SymbolObject::gather_symbol_roots(roots);
}
Value Interpreter::call(Function& function, Value this_value, Optional<MarkedValueList> arguments)

View file

@ -54,6 +54,8 @@
#include <LibJS/Runtime/Shape.h>
#include <LibJS/Runtime/StringConstructor.h>
#include <LibJS/Runtime/StringPrototype.h>
#include <LibJS/Runtime/SymbolConstructor.h>
#include <LibJS/Runtime/SymbolPrototype.h>
#include <LibJS/Runtime/Value.h>
namespace JS {
@ -102,6 +104,7 @@ void GlobalObject::initialize()
add_constructor("Number", m_number_constructor, *m_number_prototype);
add_constructor("Object", m_object_constructor, *m_object_prototype);
add_constructor("String", m_string_constructor, *m_string_prototype);
add_constructor("Symbol", m_symbol_constructor, *m_symbol_prototype);
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName) \
add_constructor(#ClassName, m_##snake_name##_constructor, *m_##snake_name##_prototype);

View file

@ -89,6 +89,7 @@ public:
virtual bool is_bound_function() const { return false; }
virtual bool is_native_property() const { return false; }
virtual bool is_string_object() const { return false; }
virtual bool is_symbol_object() const { return false; }
virtual const char* class_name() const override { return "Object"; }
virtual void visit_children(Cell::Visitor&) override;

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibJS/Heap/Heap.h>
#include <LibJS/Interpreter.h>
#include <LibJS/Runtime/Symbol.h>
namespace JS {
Symbol::Symbol(String description, bool is_global)
: m_description(move(description))
, m_is_global(is_global)
{
}
Symbol::~Symbol()
{
}
Symbol* js_symbol(Heap& heap, String description, bool is_global)
{
return heap.allocate<Symbol>(move(description), is_global);
}
Symbol* js_symbol(Interpreter& interpreter, String description, bool is_global)
{
return js_symbol(interpreter.heap(), description, is_global);
}
}

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <AK/String.h>
#include <LibJS/Runtime/Cell.h>
namespace JS {
class Symbol final : public Cell {
public:
Symbol(String, bool);
virtual ~Symbol();
const String& description() const { return m_description; }
bool is_global() const { return m_is_global; }
const String to_string() const { return String::format("Symbol(%s)", description().characters()); }
private:
virtual const char* class_name() const override { return "Symbol"; }
const String m_description;
const bool m_is_global;
};
Symbol* js_symbol(Heap&, String description, bool is_global);
Symbol* js_symbol(Interpreter&, String description, bool is_global);
}

View file

@ -0,0 +1,105 @@
/*
* Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibJS/Interpreter.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/SymbolConstructor.h>
#include <LibJS/Runtime/SymbolObject.h>
namespace JS {
SymbolConstructor::SymbolConstructor()
: NativeFunction("Symbol", *interpreter().global_object().function_prototype())
{
put("prototype", interpreter().global_object().symbol_prototype(), 0);
put("length", Value(0), Attribute::Configurable);
put_native_function("for", for_, 1, Attribute::Writable | Attribute::Configurable);
put_native_function("keyFor", key_for, 1, Attribute::Writable | Attribute::Configurable);
SymbolObject::initialize_well_known_symbols(interpreter());
put("iterator", SymbolObject::well_known_iterator(), 0);
put("asyncIterator", SymbolObject::well_known_async_terator(), 0);
put("match", SymbolObject::well_known_match(), 0);
put("matchAll", SymbolObject::well_known_match_all(), 0);
put("replace", SymbolObject::well_known_replace(), 0);
put("search", SymbolObject::well_known_search(), 0);
put("split", SymbolObject::well_known_split(), 0);
put("hasInstance", SymbolObject::well_known_has_instance(), 0);
put("isConcatSpreadable", SymbolObject::well_known_is_concat_spreadable(), 0);
put("unscopables", SymbolObject::well_known_unscopables(), 0);
put("species", SymbolObject::well_known_species(), 0);
put("toPrimitive", SymbolObject::well_known_to_primtive(), 0);
put("toStringTag", SymbolObject::well_known_to_string_tag(), 0);
}
SymbolConstructor::~SymbolConstructor()
{
}
Value SymbolConstructor::call(Interpreter& interpreter)
{
if (!interpreter.argument_count())
return js_symbol(interpreter, "", false);
return js_symbol(interpreter, interpreter.argument(0).to_string(interpreter), false);
}
Value SymbolConstructor::construct(Interpreter& interpreter)
{
interpreter.throw_exception<TypeError>("Symbol is not a constructor");
return {};
}
Value SymbolConstructor::for_(Interpreter& interpreter)
{
String description;
if (!interpreter.argument_count()) {
description = "undefined";
} else {
description = interpreter.argument(0).to_string(interpreter);
}
return SymbolObject::get_global(interpreter, description);
}
Value SymbolConstructor::key_for(Interpreter& interpreter)
{
auto argument = interpreter.argument(0);
if (!argument.is_symbol()) {
interpreter.throw_exception<TypeError>(String::format("%s is not a symbol", argument.to_string_without_side_effects().characters()));
return {};
}
auto& symbol = argument.as_symbol();
if (symbol.is_global())
return js_string(interpreter, symbol.description());
return js_undefined();
}
}

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <LibJS/Runtime/NativeFunction.h>
namespace JS {
class SymbolConstructor final : public NativeFunction {
public:
SymbolConstructor();
virtual ~SymbolConstructor() override;
virtual Value call(Interpreter&) override;
virtual Value construct(Interpreter&) override;
private:
virtual bool has_constructor() const override { return true; }
virtual const char* class_name() const override { return "SymbolConstructor"; }
static Value for_(Interpreter&);
static Value key_for(Interpreter&);
};
}

View file

@ -0,0 +1,122 @@
/*
* Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibJS/Heap/Heap.h>
#include <LibJS/Interpreter.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Symbol.h>
#include <LibJS/Runtime/SymbolObject.h>
#include <LibJS/Runtime/SymbolPrototype.h>
#include <LibJS/Runtime/Value.h>
namespace JS {
HashMap<String, Value> SymbolObject::s_global_symbol_map;
Value SymbolObject::s_well_known_iterator;
Value SymbolObject::s_well_known_async_terator;
Value SymbolObject::s_well_known_match;
Value SymbolObject::s_well_known_match_all;
Value SymbolObject::s_well_known_replace;
Value SymbolObject::s_well_known_search;
Value SymbolObject::s_well_known_split;
Value SymbolObject::s_well_known_has_instance;
Value SymbolObject::s_well_known_is_concat_spreadable;
Value SymbolObject::s_well_known_unscopables;
Value SymbolObject::s_well_known_species;
Value SymbolObject::s_well_known_to_primtive;
Value SymbolObject::s_well_known_to_string_tag;
SymbolObject* SymbolObject::create(GlobalObject& global_object, Symbol& primitive_symbol)
{
return global_object.heap().allocate<SymbolObject>(primitive_symbol, *global_object.symbol_prototype());
}
SymbolObject::SymbolObject(Symbol& symbol, Object& prototype)
: Object(&prototype)
, m_symbol(symbol)
{
}
SymbolObject::~SymbolObject()
{
}
Value SymbolObject::get_global(Interpreter& interpreter, String description)
{
auto global_symbol = s_global_symbol_map.get(description);
if (global_symbol.has_value())
return global_symbol.value();
auto symbol = js_symbol(interpreter, description, true);
s_global_symbol_map.set(description, symbol);
return Value(symbol);
}
void SymbolObject::initialize_well_known_symbols(Interpreter& interpreter)
{
SymbolObject::s_well_known_iterator = Value(js_symbol(interpreter, "Symbol.iterator", false));
SymbolObject::s_well_known_async_terator = Value(js_symbol(interpreter, "Symbol.asyncIterator", false));
SymbolObject::s_well_known_match = Value(js_symbol(interpreter, "Symbol.match", false));
SymbolObject::s_well_known_match_all = Value(js_symbol(interpreter, "Symbol.matchAll", false));
SymbolObject::s_well_known_replace = Value(js_symbol(interpreter, "Symbol.replace", false));
SymbolObject::s_well_known_search = Value(js_symbol(interpreter, "Symbol.search", false));
SymbolObject::s_well_known_split = Value(js_symbol(interpreter, "Symbol.split", false));
SymbolObject::s_well_known_has_instance = Value(js_symbol(interpreter, "Symbol.hasInstance", false));
SymbolObject::s_well_known_is_concat_spreadable = Value(js_symbol(interpreter, "Symbol.isConcatSpreadable", false));
SymbolObject::s_well_known_unscopables = Value(js_symbol(interpreter, "Symbol.unscopables", false));
SymbolObject::s_well_known_species = Value(js_symbol(interpreter, "Symbol.species", false));
SymbolObject::s_well_known_to_primtive = Value(js_symbol(interpreter, "Symbol.toPrimitive", false));
SymbolObject::s_well_known_to_string_tag = Value(js_symbol(interpreter, "Symbol.toStringTag", false));
}
void SymbolObject::gather_symbol_roots(HashTable<Cell*>& roots)
{
for (auto& global_symbol : s_global_symbol_map)
roots.set(&global_symbol.value.as_symbol());
roots.set(&s_well_known_iterator.as_symbol());
roots.set(&s_well_known_async_terator.as_symbol());
roots.set(&s_well_known_match.as_symbol());
roots.set(&s_well_known_match_all.as_symbol());
roots.set(&s_well_known_replace.as_symbol());
roots.set(&s_well_known_search.as_symbol());
roots.set(&s_well_known_split.as_symbol());
roots.set(&s_well_known_has_instance.as_symbol());
roots.set(&s_well_known_is_concat_spreadable.as_symbol());
roots.set(&s_well_known_unscopables.as_symbol());
roots.set(&s_well_known_species.as_symbol());
roots.set(&s_well_known_to_primtive.as_symbol());
roots.set(&s_well_known_to_string_tag.as_symbol());
}
void SymbolObject::visit_children(Cell::Visitor& visitor)
{
Object::visit_children(visitor);
visitor.visit(&m_symbol);
}
}

View file

@ -0,0 +1,95 @@
/*
* Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Symbol.h>
namespace JS {
class SymbolObject : public Object {
public:
static SymbolObject* create(GlobalObject&, Symbol&);
SymbolObject(Symbol&, Object& prototype);
virtual ~SymbolObject() override;
Symbol& primitive_symbol() { return m_symbol; }
const Symbol& primitive_symbol() const { return m_symbol; }
const String& description() const { return m_symbol.description(); }
bool is_global() const { return m_symbol.is_global(); }
static Value get_global(Interpreter&, String description);
virtual Value value_of() const override
{
return Value(&m_symbol);
}
static void initialize_well_known_symbols(Interpreter&);
static void gather_symbol_roots(HashTable<Cell*>& roots);
static Value well_known_iterator() { return s_well_known_iterator; };
static Value well_known_async_terator() { return s_well_known_async_terator; };
static Value well_known_match() { return s_well_known_match; };
static Value well_known_match_all() { return s_well_known_match_all; };
static Value well_known_replace() { return s_well_known_replace; };
static Value well_known_search() { return s_well_known_search; };
static Value well_known_split() { return s_well_known_split; };
static Value well_known_has_instance() { return s_well_known_has_instance; };
static Value well_known_is_concat_spreadable() { return s_well_known_is_concat_spreadable; };
static Value well_known_unscopables() { return s_well_known_unscopables; };
static Value well_known_species() { return s_well_known_species; };
static Value well_known_to_primtive() { return s_well_known_to_primtive; };
static Value well_known_to_string_tag() { return s_well_known_to_string_tag; };
private:
virtual void visit_children(Visitor&) override;
virtual const char* class_name() const override { return "SymbolObject"; }
virtual bool is_symbol_object() const override { return true; }
Symbol& m_symbol;
static HashMap<String, Value> s_global_symbol_map;
static Value s_well_known_iterator;
static Value s_well_known_async_terator;
static Value s_well_known_match;
static Value s_well_known_match_all;
static Value s_well_known_replace;
static Value s_well_known_search;
static Value s_well_known_split;
static Value s_well_known_has_instance;
static Value s_well_known_is_concat_spreadable;
static Value s_well_known_unscopables;
static Value s_well_known_species;
static Value s_well_known_to_primtive;
static Value s_well_known_to_string_tag;
};
}

View file

@ -0,0 +1,98 @@
/*
* Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/Function.h>
#include <AK/StringBuilder.h>
#include <LibJS/Heap/Heap.h>
#include <LibJS/Interpreter.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/PrimitiveString.h>
#include <LibJS/Runtime/SymbolObject.h>
#include <LibJS/Runtime/SymbolPrototype.h>
#include <LibJS/Runtime/Value.h>
#include <string.h>
namespace JS {
SymbolPrototype::SymbolPrototype()
: Object(interpreter().global_object().object_prototype())
{
// FIXME: description has no setter, eventually remove
put_native_property("description", description_getter, description_setter, Attribute::Configurable);
put_native_function("toString", to_string, 0, Attribute::Writable | Attribute::Configurable);
put_native_function("valueOf", value_of, 0, Attribute::Writable | Attribute::Configurable);
}
SymbolPrototype::~SymbolPrototype()
{
}
static SymbolObject* this_symbol_from_interpreter(Interpreter& interpreter)
{
auto* this_object = interpreter.this_value().to_object(interpreter.heap());
if (!this_object)
return nullptr;
if (!this_object->is_symbol_object()) {
interpreter.throw_exception<TypeError>("object must be of type Symbol");
return nullptr;
}
return static_cast<SymbolObject*>(this_object);
}
Value SymbolPrototype::description_getter(Interpreter& interpreter)
{
auto* this_object = this_symbol_from_interpreter(interpreter);
if (!this_object)
return {};
return js_string(interpreter, this_object->description());
}
void SymbolPrototype::description_setter(Interpreter&, Value)
{
// No-op, remove eventually
}
Value SymbolPrototype::to_string(Interpreter& interpreter)
{
auto* this_object = this_symbol_from_interpreter(interpreter);
if (!this_object)
return {};
auto string = this_object->primitive_symbol().to_string();
return js_string(interpreter, move(string));
}
Value SymbolPrototype::value_of(Interpreter& interpreter)
{
auto* this_object = this_symbol_from_interpreter(interpreter);
if (!this_object)
return {};
return this_object->value_of();
}
}

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <LibJS/Runtime/Object.h>
namespace JS {
class SymbolPrototype final : public Object {
public:
SymbolPrototype();
virtual ~SymbolPrototype() override;
private:
virtual const char* class_name() const override { return "SymbolPrototype"; }
static Value description_getter(Interpreter&);
static void description_setter(Interpreter&, Value);
static Value to_string(Interpreter&);
static Value value_of(Interpreter&);
};
}

View file

@ -36,7 +36,9 @@
#include <LibJS/Runtime/NumberObject.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/PrimitiveString.h>
#include <LibJS/Runtime/Symbol.h>
#include <LibJS/Runtime/StringObject.h>
#include <LibJS/Runtime/SymbolObject.h>
#include <LibJS/Runtime/Value.h>
#include <math.h>
@ -87,6 +89,9 @@ String Value::to_string_without_side_effects() const
if (is_string())
return m_value.as_string->string();
if (is_symbol())
return as_symbol().to_string();
ASSERT(is_object());
return String::format("[object %s]", as_object().class_name());
}
@ -124,6 +129,11 @@ String Value::to_string(Interpreter& interpreter) const
return String::format("%.4f", as_double());
}
if (is_symbol()) {
interpreter.throw_exception<TypeError>("Can't convert symbol to string");
return {};
}
if (is_object()) {
auto primitive_value = as_object().to_primitive(Object::PreferredType::String);
// FIXME: Maybe we should pass in the Interpreter& and call interpreter.exception() instead?
@ -152,6 +162,7 @@ bool Value::to_boolean() const
case Type::String:
return !as_string().string().is_empty();
case Type::Object:
case Type::Symbol:
return true;
default:
ASSERT_NOT_REACHED();
@ -173,6 +184,9 @@ Object* Value::to_object(Heap& heap) const
if (is_string())
return StringObject::create(heap.interpreter().global_object(), *m_value.as_string);
if (is_symbol())
return SymbolObject::create(heap.interpreter().global_object(), *m_value.as_symbol);
if (is_number())
return NumberObject::create(heap.interpreter().global_object(), m_value.as_double);
@ -216,6 +230,9 @@ Value Value::to_number() const
return js_nan();
return Value(parsed_double);
}
case Type::Symbol:
// FIXME: Get access to the interpreter and throw a TypeError
ASSERT_NOT_REACHED();
case Type::Object:
return m_value.as_object->to_primitive(Object::PreferredType::Number).to_number();
}
@ -473,6 +490,8 @@ bool same_value_non_numeric(Interpreter&, Value lhs, Value rhs)
return true;
case Value::Type::String:
return lhs.as_string().string() == rhs.as_string().string();
case Value::Type::Symbol:
return &lhs.as_symbol() == &rhs.as_symbol();
case Value::Type::Boolean:
return lhs.as_bool() == rhs.as_bool();
case Value::Type::Object:
@ -520,10 +539,10 @@ bool abstract_eq(Interpreter& interpreter, Value lhs, Value rhs)
if (rhs.is_boolean())
return abstract_eq(interpreter, lhs, rhs.to_number());
if ((lhs.is_string() || lhs.is_number()) && rhs.is_object())
if ((lhs.is_string() || lhs.is_number() || lhs.is_symbol()) && rhs.is_object())
return abstract_eq(interpreter, lhs, rhs.to_primitive(interpreter));
if (lhs.is_object() && (rhs.is_string() || rhs.is_number()))
if (lhs.is_object() && (rhs.is_string() || rhs.is_number() || rhs.is_symbol()))
return abstract_eq(interpreter, lhs.to_primitive(interpreter), rhs);
return false;

View file

@ -30,6 +30,7 @@
#include <AK/Forward.h>
#include <AK/LogStream.h>
#include <LibJS/Forward.h>
#include <LibJS/Runtime/Symbol.h>
namespace JS {
@ -43,6 +44,7 @@ public:
String,
Object,
Boolean,
Symbol,
};
bool is_empty() const { return m_type == Type::Empty; }
@ -52,6 +54,7 @@ public:
bool is_string() const { return m_type == Type::String; }
bool is_object() const { return m_type == Type::Object; }
bool is_boolean() const { return m_type == Type::Boolean; }
bool is_symbol() const { return m_type == Type::Symbol; }
bool is_cell() const { return is_string() || is_object(); }
bool is_array() const;
bool is_function() const;
@ -110,6 +113,12 @@ public:
m_value.as_string = string;
}
Value(Symbol* symbol)
: m_type(Type::Symbol)
{
m_value.as_symbol = symbol;
}
explicit Value(Type type)
: m_type(type)
{
@ -153,6 +162,18 @@ public:
return *m_value.as_string;
}
Symbol& as_symbol()
{
ASSERT(is_symbol());
return *m_value.as_symbol;
}
const Symbol& as_symbol() const
{
ASSERT(is_symbol());
return *m_value.as_symbol;
}
Cell* as_cell()
{
ASSERT(is_cell());
@ -188,6 +209,7 @@ private:
bool as_bool;
double as_double;
PrimitiveString* as_string;
Symbol* as_symbol;
Object* as_object;
Cell* as_cell;
} m_value;

View file

@ -0,0 +1,30 @@
load("test-common.js")
try {
const localSym = Symbol("foo");
const globalSym = Symbol.for("foo");
assert(localSym !== globalSym);
assert(localSym !== Symbol("foo"));
assert(globalSym !== Symbol("foo"));
assert(globalSym === Symbol.for("foo"));
assert(localSym.toString() === "Symbol(foo)");
assert(globalSym.toString() === "Symbol(foo)");
assert(Symbol.for(1).description === "1");
assert(Symbol.for(true).description === "true");
assert(Symbol.for({}).description === "[object Object]");
assert(Symbol.for().description === "undefined");
assert(Symbol.for(null).description === "null");
assertThrowsError(() => {
Symbol.for(Symbol());
}, {
error: TypeError,
message: "Can't convert symbol to string",
});
console.log("PASS");
} catch (e) {
console.log("FAIL: " + e);
}

View file

@ -0,0 +1,26 @@
load("test-common.js")
try {
const s1 = Symbol("foo");
const s2 = Symbol("foo");
assert(s1 !== s2);
assert(s1.description === "foo");
assert(s2.description === "foo");
s1.description = "bar";
assert(s1.description === "foo");
assert(typeof s1 === "symbol");
assertThrowsError(() => {
Symbol(Symbol('foo'));
}, {
error: TypeError,
message: "Can't convert symbol to string"
})
console.log("PASS");
} catch (e) {
console.log("FAIL: " + e);
}

View file

@ -0,0 +1,31 @@
load("test-common.js")
try {
const localSym = Symbol("foo");
const globalSym = Symbol.for("foo");
assert(Symbol.keyFor(localSym) === undefined);
assert(Symbol.keyFor(globalSym) === "foo");
const testThrows = (value, str) => {
assertThrowsError(() => {
Symbol.keyFor(value);
}, {
error: TypeError,
message: str + " is not a symbol",
});
};
testThrows(1, "1");
testThrows(null, "null");
testThrows(undefined, "undefined");
testThrows([], "[object Array]");
testThrows({}, "[object Object]");
testThrows(true, "true");
testThrows("foobar", "foobar");
testThrows(function(){}, "[object ScriptFunction]"); // FIXME: Better function stringification
console.log("PASS");
} catch (e) {
console.log("FAIL: " + e);
}

View file

@ -0,0 +1,28 @@
load("test-common.js")
try {
const s1 = Symbol("foo");
const s2 = Symbol.for("bar");
assert(s1.toString() === "Symbol(foo)");
assert(s2.toString() === "Symbol(bar)");
assertThrowsError(() => {
s1 + "";
}, {
error: TypeError,
message: "Can't convert symbol to string",
});
// FIXME: Uncomment when this doesn't assert
// assertThrowsError(() => {
// s1 + 1;
// }, {
// error: TypeError,
// message: "Can't convert symbol to number",
// });
console.log("PASS");
} catch (e) {
console.log("FAIL: " + e);
}

View file

@ -0,0 +1,22 @@
load("test-common.js");
try {
let local = Symbol('foo');
let global = Symbol.for('foo');
assert(local.valueOf() === local);
assert(global.valueOf() === global);
assert(Symbol.prototype.valueOf.call(local) === local);
assert(Symbol.prototype.valueOf.call(global) === global);
assertThrowsError(() => {
Symbol.prototype.valueOf.call("foo");
}, {
error: TypeError,
message: "object must be of type Symbol"
});
console.log("PASS");
} catch (err) {
console.log("FAIL: " + err);
}