1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 07:27:45 +00:00

Libraries: Move to Userland/Libraries/

This commit is contained in:
Andreas Kling 2021-01-12 12:17:30 +01:00
parent dc28c07fa5
commit 13d7c09125
1857 changed files with 266 additions and 274 deletions

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,88 @@
set(SOURCES
AST.cpp
Console.cpp
Heap/Allocator.cpp
Heap/Handle.cpp
Heap/HeapBlock.cpp
Heap/Heap.cpp
Interpreter.cpp
Lexer.cpp
MarkupGenerator.cpp
Parser.cpp
Runtime/Array.cpp
Runtime/ArrayBuffer.cpp
Runtime/ArrayBufferConstructor.cpp
Runtime/ArrayBufferPrototype.cpp
Runtime/ArrayConstructor.cpp
Runtime/ArrayIterator.cpp
Runtime/ArrayIteratorPrototype.cpp
Runtime/ArrayPrototype.cpp
Runtime/BigInt.cpp
Runtime/BigIntConstructor.cpp
Runtime/BigIntObject.cpp
Runtime/BigIntPrototype.cpp
Runtime/BooleanConstructor.cpp
Runtime/BooleanObject.cpp
Runtime/BooleanPrototype.cpp
Runtime/BoundFunction.cpp
Runtime/Cell.cpp
Runtime/ConsoleObject.cpp
Runtime/DateConstructor.cpp
Runtime/Date.cpp
Runtime/DatePrototype.cpp
Runtime/ErrorConstructor.cpp
Runtime/Error.cpp
Runtime/ErrorPrototype.cpp
Runtime/ErrorTypes.cpp
Runtime/Exception.cpp
Runtime/FunctionConstructor.cpp
Runtime/Function.cpp
Runtime/FunctionPrototype.cpp
Runtime/GlobalObject.cpp
Runtime/IndexedProperties.cpp
Runtime/IteratorOperations.cpp
Runtime/IteratorPrototype.cpp
Runtime/JSONObject.cpp
Runtime/LexicalEnvironment.cpp
Runtime/MarkedValueList.cpp
Runtime/MathObject.cpp
Runtime/NativeFunction.cpp
Runtime/NativeProperty.cpp
Runtime/NumberConstructor.cpp
Runtime/NumberObject.cpp
Runtime/NumberPrototype.cpp
Runtime/ObjectConstructor.cpp
Runtime/Object.cpp
Runtime/ObjectPrototype.cpp
Runtime/PrimitiveString.cpp
Runtime/ProxyConstructor.cpp
Runtime/ProxyObject.cpp
Runtime/Reference.cpp
Runtime/ReflectObject.cpp
Runtime/RegExpConstructor.cpp
Runtime/RegExpObject.cpp
Runtime/RegExpPrototype.cpp
Runtime/ScopeObject.cpp
Runtime/ScriptFunction.cpp
Runtime/Shape.cpp
Runtime/StringConstructor.cpp
Runtime/StringIterator.cpp
Runtime/StringIteratorPrototype.cpp
Runtime/StringObject.cpp
Runtime/StringPrototype.cpp
Runtime/Symbol.cpp
Runtime/SymbolConstructor.cpp
Runtime/SymbolObject.cpp
Runtime/SymbolPrototype.cpp
Runtime/TypedArray.cpp
Runtime/TypedArrayConstructor.cpp
Runtime/TypedArrayPrototype.cpp
Runtime/Uint8ClampedArray.cpp
Runtime/VM.cpp
Runtime/Value.cpp
Runtime/WithScope.cpp
Token.cpp
)
serenity_lib(LibJS js)
target_link_libraries(LibJS LibM LibCore LibCrypto LibRegex)

View file

@ -0,0 +1,138 @@
/*
* Copyright (c) 2020, Emanuele Torre <torreemanuele6@gmail.com>
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* 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/Console.h>
#include <LibJS/Runtime/GlobalObject.h>
namespace JS {
Console::Console(GlobalObject& global_object)
: m_global_object(global_object)
{
}
Value Console::debug()
{
if (m_client)
return m_client->debug();
return js_undefined();
}
Value Console::error()
{
if (m_client)
return m_client->error();
return js_undefined();
}
Value Console::info()
{
if (m_client)
return m_client->info();
return js_undefined();
}
Value Console::log()
{
if (m_client)
return m_client->log();
return js_undefined();
}
Value Console::warn()
{
if (m_client)
return m_client->warn();
return js_undefined();
}
Value Console::clear()
{
if (m_client)
return m_client->clear();
return js_undefined();
}
Value Console::trace()
{
if (m_client)
return m_client->trace();
return js_undefined();
}
Value Console::count()
{
if (m_client)
return m_client->count();
return js_undefined();
}
Value Console::count_reset()
{
if (m_client)
return m_client->count_reset();
return js_undefined();
}
unsigned Console::counter_increment(String label)
{
auto value = m_counters.get(label);
if (!value.has_value()) {
m_counters.set(label, 1);
return 1;
}
auto new_value = value.value() + 1;
m_counters.set(label, new_value);
return new_value;
}
bool Console::counter_reset(String label)
{
if (!m_counters.contains(label))
return false;
m_counters.remove(label);
return true;
}
VM& ConsoleClient::vm()
{
return global_object().vm();
}
Vector<String> ConsoleClient::get_trace() const
{
Vector<String> trace;
auto& call_stack = m_console.global_object().vm().call_stack();
// -2 to skip the console.trace() call frame
for (ssize_t i = call_stack.size() - 2; i >= 0; --i)
trace.append(call_stack[i]->function_name);
return trace;
}
}

View file

@ -0,0 +1,104 @@
/*
* Copyright (c) 2020, Emanuele Torre <torreemanuele6@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/Function.h>
#include <AK/HashMap.h>
#include <AK/Noncopyable.h>
#include <LibJS/Forward.h>
namespace JS {
class ConsoleClient;
class Console {
AK_MAKE_NONCOPYABLE(Console);
AK_MAKE_NONMOVABLE(Console);
public:
explicit Console(GlobalObject&);
void set_client(ConsoleClient& client) { m_client = &client; }
GlobalObject& global_object() { return m_global_object; }
const GlobalObject& global_object() const { return m_global_object; }
HashMap<String, unsigned>& counters() { return m_counters; }
const HashMap<String, unsigned>& counters() const { return m_counters; }
Value debug();
Value error();
Value info();
Value log();
Value warn();
Value clear();
Value trace();
Value count();
Value count_reset();
unsigned counter_increment(String label);
bool counter_reset(String label);
private:
GlobalObject& m_global_object;
ConsoleClient* m_client { nullptr };
HashMap<String, unsigned> m_counters;
};
class ConsoleClient {
public:
ConsoleClient(Console& console)
: m_console(console)
{
}
virtual Value debug() = 0;
virtual Value error() = 0;
virtual Value info() = 0;
virtual Value log() = 0;
virtual Value warn() = 0;
virtual Value clear() = 0;
virtual Value trace() = 0;
virtual Value count() = 0;
virtual Value count_reset() = 0;
protected:
VM& vm();
GlobalObject& global_object() { return m_console.global_object(); }
const GlobalObject& global_object() const { return m_console.global_object(); }
Vector<String> get_trace() const;
Console& m_console;
};
}

View file

@ -0,0 +1,172 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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
#define JS_DECLARE_NATIVE_FUNCTION(name) \
static JS::Value name(JS::VM&, JS::GlobalObject&)
#define JS_DECLARE_NATIVE_GETTER(name) \
static JS::Value name(JS::VM&, JS::GlobalObject&)
#define JS_DECLARE_NATIVE_SETTER(name) \
static void name(JS::VM&, JS::GlobalObject&, JS::Value)
#define JS_DEFINE_NATIVE_FUNCTION(name) \
JS::Value name([[maybe_unused]] JS::VM& vm, [[maybe_unused]] JS::GlobalObject& global_object)
#define JS_DEFINE_NATIVE_GETTER(name) \
JS::Value name([[maybe_unused]] JS::VM& vm, [[maybe_unused]] JS::GlobalObject& global_object)
#define JS_DEFINE_NATIVE_SETTER(name) \
void name([[maybe_unused]] JS::VM& vm, [[maybe_unused]] JS::GlobalObject& global_object, [[maybe_unused]] JS::Value value)
// NOTE: Proxy is not included here as it doesn't have a prototype - m_proxy_constructor is initialized separately.
#define JS_ENUMERATE_NATIVE_OBJECTS_EXCLUDING_TEMPLATES \
__JS_ENUMERATE(Array, array, ArrayPrototype, ArrayConstructor, void) \
__JS_ENUMERATE(ArrayBuffer, array_buffer, ArrayBufferPrototype, ArrayBufferConstructor, void) \
__JS_ENUMERATE(BigIntObject, bigint, BigIntPrototype, BigIntConstructor, void) \
__JS_ENUMERATE(BooleanObject, boolean, BooleanPrototype, BooleanConstructor, void) \
__JS_ENUMERATE(Date, date, DatePrototype, DateConstructor, void) \
__JS_ENUMERATE(Error, error, ErrorPrototype, ErrorConstructor, void) \
__JS_ENUMERATE(Function, function, FunctionPrototype, FunctionConstructor, void) \
__JS_ENUMERATE(NumberObject, number, NumberPrototype, NumberConstructor, void) \
__JS_ENUMERATE(Object, object, ObjectPrototype, ObjectConstructor, void) \
__JS_ENUMERATE(RegExpObject, regexp, RegExpPrototype, RegExpConstructor, void) \
__JS_ENUMERATE(StringObject, string, StringPrototype, StringConstructor, void) \
__JS_ENUMERATE(SymbolObject, symbol, SymbolPrototype, SymbolConstructor, void)
#define JS_ENUMERATE_NATIVE_OBJECTS \
JS_ENUMERATE_NATIVE_OBJECTS_EXCLUDING_TEMPLATES \
__JS_ENUMERATE(TypedArray, typed_array, TypedArrayPrototype, TypedArrayConstructor, void)
#define JS_ENUMERATE_ERROR_SUBCLASSES \
__JS_ENUMERATE(EvalError, eval_error, EvalErrorPrototype, EvalErrorConstructor, void) \
__JS_ENUMERATE(InternalError, internal_error, InternalErrorPrototype, InternalErrorConstructor, void) \
__JS_ENUMERATE(InvalidCharacterError, invalid_character_error, InvalidCharacterErrorPrototype, InvalidCharacterErrorConstructor, void) \
__JS_ENUMERATE(RangeError, range_error, RangeErrorPrototype, RangeErrorConstructor, void) \
__JS_ENUMERATE(ReferenceError, reference_error, ReferenceErrorPrototype, ReferenceErrorConstructor, void) \
__JS_ENUMERATE(SyntaxError, syntax_error, SyntaxErrorPrototype, SyntaxErrorConstructor, void) \
__JS_ENUMERATE(TypeError, type_error, TypeErrorPrototype, TypeErrorConstructor, void) \
__JS_ENUMERATE(URIError, uri_error, URIErrorPrototype, URIErrorConstructor, void)
#define JS_ENUMERATE_TYPED_ARRAYS \
__JS_ENUMERATE(Uint8Array, uint8_array, Uint8ArrayPrototype, Uint8ArrayConstructor, u8) \
__JS_ENUMERATE(Uint16Array, uint16_array, Uint16ArrayPrototype, Uint16ArrayConstructor, u16) \
__JS_ENUMERATE(Uint32Array, uint32_array, Uint32ArrayPrototype, Uint32ArrayConstructor, u32) \
__JS_ENUMERATE(Int8Array, int8_array, Int8ArrayPrototype, Int8ArrayConstructor, i8) \
__JS_ENUMERATE(Int16Array, int16_array, Int16ArrayPrototype, Int16ArrayConstructor, i16) \
__JS_ENUMERATE(Int32Array, int32_array, Int32ArrayPrototype, Int32ArrayConstructor, i32) \
__JS_ENUMERATE(Float32Array, float32_array, Float32ArrayPrototype, Float32ArrayConstructor, float) \
__JS_ENUMERATE(Float64Array, float64_array, Float64ArrayPrototype, Float64ArrayConstructor, double)
#define JS_ENUMERATE_ITERATOR_PROTOTYPES \
__JS_ENUMERATE(Iterator, iterator) \
__JS_ENUMERATE(ArrayIterator, array_iterator) \
__JS_ENUMERATE(StringIterator, string_iterator)
#define JS_ENUMERATE_BUILTIN_TYPES \
JS_ENUMERATE_NATIVE_OBJECTS \
JS_ENUMERATE_ERROR_SUBCLASSES \
JS_ENUMERATE_TYPED_ARRAYS
#define JS_ENUMERATE_WELL_KNOWN_SYMBOLS \
__JS_ENUMERATE(iterator, iterator) \
__JS_ENUMERATE(asyncIterator, async_iterator) \
__JS_ENUMERATE(match, match) \
__JS_ENUMERATE(matchAll, match_all) \
__JS_ENUMERATE(replace, replace) \
__JS_ENUMERATE(search, search) \
__JS_ENUMERATE(split, split) \
__JS_ENUMERATE(hasInstance, has_instance) \
__JS_ENUMERATE(isConcatSpreadable, is_concat_spreadable) \
__JS_ENUMERATE(unscopables, unscopables) \
__JS_ENUMERATE(species, species) \
__JS_ENUMERATE(toPrimitive, to_primitive) \
__JS_ENUMERATE(toStringTag, to_string_tag)
#define JS_ENUMERATE_REGEXP_FLAGS \
__JS_ENUMERATE(global, global, g, Global) \
__JS_ENUMERATE(ignoreCase, ignore_case, i, Insensitive) \
__JS_ENUMERATE(multiline, multiline, m, Multiline) \
__JS_ENUMERATE(dotAll, dot_all, s, SingleLine) \
__JS_ENUMERATE(unicode, unicode, u, Unicode) \
__JS_ENUMERATE(sticky, sticky, y, Sticky)
namespace JS {
class ASTNode;
class Allocator;
class BigInt;
class BoundFunction;
class Cell;
class Console;
class DeferGC;
class Error;
class Exception;
class Expression;
class Accessor;
class GlobalObject;
class HandleImpl;
class Heap;
class HeapBlock;
class Interpreter;
class LexicalEnvironment;
class MarkedValueList;
class NativeProperty;
class PrimitiveString;
class Reference;
class ScopeNode;
class ScopeObject;
class Shape;
class Statement;
class Symbol;
class Token;
class Uint8ClampedArray;
class VM;
class Value;
enum class DeclarationKind;
// Not included in JS_ENUMERATE_NATIVE_OBJECTS due to missing distinct prototype
class ProxyObject;
class ProxyConstructor;
class TypedArrayConstructor;
class TypedArrayPrototype;
#define __JS_ENUMERATE(ClassName, snake_name, ConstructorName, PrototypeName, ArrayType) \
class ClassName; \
class ConstructorName; \
class PrototypeName;
JS_ENUMERATE_NATIVE_OBJECTS_EXCLUDING_TEMPLATES
JS_ENUMERATE_ERROR_SUBCLASSES
JS_ENUMERATE_TYPED_ARRAYS
#undef __JS_ENUMERATE
template<class T>
class Handle;
}

View file

@ -0,0 +1,69 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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/Badge.h>
#include <LibJS/Heap/Allocator.h>
#include <LibJS/Heap/HeapBlock.h>
namespace JS {
Allocator::Allocator(size_t cell_size)
: m_cell_size(cell_size)
{
}
Allocator::~Allocator()
{
}
Cell* Allocator::allocate_cell(Heap& heap)
{
if (m_usable_blocks.is_empty()) {
auto block = HeapBlock::create_with_cell_size(heap, m_cell_size);
m_usable_blocks.append(*block.leak_ptr());
}
auto& block = *m_usable_blocks.last();
auto* cell = block.allocate();
ASSERT(cell);
if (block.is_full())
m_full_blocks.append(*m_usable_blocks.last());
return cell;
}
void Allocator::block_did_become_empty(Badge<Heap>, HeapBlock& block)
{
block.m_list_node.remove();
delete &block;
}
void Allocator::block_did_become_usable(Badge<Heap>, HeapBlock& block)
{
ASSERT(!block.is_full());
m_usable_blocks.append(block);
}
}

View file

@ -0,0 +1,71 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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/IntrusiveList.h>
#include <AK/NonnullOwnPtr.h>
#include <AK/Vector.h>
#include <LibJS/Forward.h>
#include <LibJS/Heap/HeapBlock.h>
namespace JS {
class Allocator {
public:
Allocator(size_t cell_size);
~Allocator();
size_t cell_size() const { return m_cell_size; }
Cell* allocate_cell(Heap&);
template<typename Callback>
IterationDecision for_each_block(Callback callback)
{
for (auto& block : m_full_blocks) {
if (callback(block) == IterationDecision::Break)
return IterationDecision::Break;
}
for (auto& block : m_usable_blocks) {
if (callback(block) == IterationDecision::Break)
return IterationDecision::Break;
}
return IterationDecision::Continue;
}
void block_did_become_empty(Badge<Heap>, HeapBlock&);
void block_did_become_usable(Badge<Heap>, HeapBlock&);
private:
const size_t m_cell_size;
typedef IntrusiveList<HeapBlock, &HeapBlock::m_list_node> BlockList;
BlockList m_full_blocks;
BlockList m_usable_blocks;
};
}

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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/Heap/Heap.h>
namespace JS {
class DeferGC {
public:
explicit DeferGC(Heap& heap)
: m_heap(heap)
{
m_heap.defer_gc({});
}
~DeferGC()
{
m_heap.undefer_gc({});
}
private:
Heap& m_heap;
};
}

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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/Handle.h>
#include <LibJS/Heap/Heap.h>
#include <LibJS/Runtime/Cell.h>
namespace JS {
HandleImpl::HandleImpl(Cell* cell)
: m_cell(cell)
{
m_cell->heap().did_create_handle({}, *this);
}
HandleImpl::~HandleImpl()
{
m_cell->heap().did_destroy_handle({}, *this);
}
}

View file

@ -0,0 +1,85 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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/Badge.h>
#include <AK/Noncopyable.h>
#include <AK/RefCounted.h>
#include <AK/RefPtr.h>
#include <LibJS/Forward.h>
namespace JS {
class HandleImpl : public RefCounted<HandleImpl> {
AK_MAKE_NONCOPYABLE(HandleImpl);
AK_MAKE_NONMOVABLE(HandleImpl);
public:
~HandleImpl();
Cell* cell() { return m_cell; }
const Cell* cell() const { return m_cell; }
private:
template<class T>
friend class Handle;
explicit HandleImpl(Cell*);
Cell* m_cell { nullptr };
};
template<class T>
class Handle {
public:
Handle() { }
static Handle create(T* cell)
{
return Handle(adopt(*new HandleImpl(cell)));
}
T* cell() { return static_cast<T*>(m_impl->cell()); }
const T* cell() const { return static_cast<const T*>(m_impl->cell()); }
bool is_null() const { return m_impl.is_null(); }
private:
explicit Handle(NonnullRefPtr<HandleImpl> impl)
: m_impl(move(impl))
{
}
RefPtr<HandleImpl> m_impl;
};
template<class T>
inline Handle<T> make_handle(T* cell)
{
return Handle<T>::create(cell);
}
}

View file

@ -0,0 +1,331 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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/Badge.h>
#include <AK/HashTable.h>
#include <AK/StackInfo.h>
#include <AK/TemporaryChange.h>
#include <LibCore/ElapsedTimer.h>
#include <LibJS/Heap/Allocator.h>
#include <LibJS/Heap/Handle.h>
#include <LibJS/Heap/Heap.h>
#include <LibJS/Heap/HeapBlock.h>
#include <LibJS/Interpreter.h>
#include <LibJS/Runtime/Object.h>
#include <setjmp.h>
//#define HEAP_DEBUG
namespace JS {
Heap::Heap(VM& vm)
: m_vm(vm)
{
m_allocators.append(make<Allocator>(16));
m_allocators.append(make<Allocator>(32));
m_allocators.append(make<Allocator>(64));
m_allocators.append(make<Allocator>(128));
m_allocators.append(make<Allocator>(256));
m_allocators.append(make<Allocator>(512));
m_allocators.append(make<Allocator>(1024));
m_allocators.append(make<Allocator>(3172));
}
Heap::~Heap()
{
collect_garbage(CollectionType::CollectEverything);
}
ALWAYS_INLINE Allocator& Heap::allocator_for_size(size_t cell_size)
{
for (auto& allocator : m_allocators) {
if (allocator->cell_size() >= cell_size)
return *allocator;
}
ASSERT_NOT_REACHED();
}
Cell* Heap::allocate_cell(size_t size)
{
if (should_collect_on_every_allocation()) {
collect_garbage();
} else if (m_allocations_since_last_gc > m_max_allocations_between_gc) {
m_allocations_since_last_gc = 0;
collect_garbage();
} else {
++m_allocations_since_last_gc;
}
auto& allocator = allocator_for_size(size);
return allocator.allocate_cell(*this);
}
void Heap::collect_garbage(CollectionType collection_type, bool print_report)
{
ASSERT(!m_collecting_garbage);
TemporaryChange change(m_collecting_garbage, true);
Core::ElapsedTimer collection_measurement_timer;
collection_measurement_timer.start();
if (collection_type == CollectionType::CollectGarbage) {
if (m_gc_deferrals) {
m_should_gc_when_deferral_ends = true;
return;
}
HashTable<Cell*> roots;
gather_roots(roots);
mark_live_cells(roots);
}
sweep_dead_cells(print_report, collection_measurement_timer);
}
void Heap::gather_roots(HashTable<Cell*>& roots)
{
vm().gather_roots(roots);
gather_conservative_roots(roots);
for (auto* handle : m_handles)
roots.set(handle->cell());
for (auto* list : m_marked_value_lists) {
for (auto& value : list->values()) {
if (value.is_cell())
roots.set(value.as_cell());
}
}
#ifdef HEAP_DEBUG
dbgln("gather_roots:");
for (auto* root : roots)
dbgln(" + {}", root);
#endif
}
__attribute__((no_sanitize("address"))) void Heap::gather_conservative_roots(HashTable<Cell*>& roots)
{
FlatPtr dummy;
#ifdef HEAP_DEBUG
dbgln("gather_conservative_roots:");
#endif
jmp_buf buf;
setjmp(buf);
HashTable<FlatPtr> possible_pointers;
const FlatPtr* raw_jmp_buf = reinterpret_cast<const FlatPtr*>(buf);
for (size_t i = 0; i < ((size_t)sizeof(buf)) / sizeof(FlatPtr); i += sizeof(FlatPtr))
possible_pointers.set(raw_jmp_buf[i]);
FlatPtr stack_reference = reinterpret_cast<FlatPtr>(&dummy);
auto& stack_info = m_vm.stack_info();
for (FlatPtr stack_address = stack_reference; stack_address < stack_info.top(); stack_address += sizeof(FlatPtr)) {
auto data = *reinterpret_cast<FlatPtr*>(stack_address);
possible_pointers.set(data);
}
HashTable<HeapBlock*> all_live_heap_blocks;
for_each_block([&](auto& block) {
all_live_heap_blocks.set(&block);
return IterationDecision::Continue;
});
for (auto possible_pointer : possible_pointers) {
if (!possible_pointer)
continue;
#ifdef HEAP_DEBUG
dbgln(" ? {}", (const void*)possible_pointer);
#endif
auto* possible_heap_block = HeapBlock::from_cell(reinterpret_cast<const Cell*>(possible_pointer));
if (all_live_heap_blocks.contains(possible_heap_block)) {
if (auto* cell = possible_heap_block->cell_from_possible_pointer(possible_pointer)) {
if (cell->is_live()) {
#ifdef HEAP_DEBUG
dbgln(" ?-> {}", (const void*)cell);
#endif
roots.set(cell);
} else {
#ifdef HEAP_DEBUG
dbgln(" #-> {}", (const void*)cell);
#endif
}
}
}
}
}
class MarkingVisitor final : public Cell::Visitor {
public:
MarkingVisitor() { }
virtual void visit_impl(Cell* cell)
{
if (cell->is_marked())
return;
#ifdef HEAP_DEBUG
dbgln(" ! {}", cell);
#endif
cell->set_marked(true);
cell->visit_edges(*this);
}
};
void Heap::mark_live_cells(const HashTable<Cell*>& roots)
{
#ifdef HEAP_DEBUG
dbgln("mark_live_cells:");
#endif
MarkingVisitor visitor;
for (auto* root : roots)
visitor.visit(root);
}
void Heap::sweep_dead_cells(bool print_report, const Core::ElapsedTimer& measurement_timer)
{
#ifdef HEAP_DEBUG
dbgln("sweep_dead_cells:");
#endif
Vector<HeapBlock*, 32> empty_blocks;
Vector<HeapBlock*, 32> full_blocks_that_became_usable;
size_t collected_cells = 0;
size_t live_cells = 0;
size_t collected_cell_bytes = 0;
size_t live_cell_bytes = 0;
for_each_block([&](auto& block) {
bool block_has_live_cells = false;
bool block_was_full = block.is_full();
block.for_each_cell([&](Cell* cell) {
if (cell->is_live()) {
if (!cell->is_marked()) {
#ifdef HEAP_DEBUG
dbgln(" ~ {}", cell);
#endif
block.deallocate(cell);
++collected_cells;
collected_cell_bytes += block.cell_size();
} else {
cell->set_marked(false);
block_has_live_cells = true;
++live_cells;
live_cell_bytes += block.cell_size();
}
}
});
if (!block_has_live_cells)
empty_blocks.append(&block);
else if (block_was_full != block.is_full())
full_blocks_that_became_usable.append(&block);
return IterationDecision::Continue;
});
for (auto* block : empty_blocks) {
#ifdef HEAP_DEBUG
dbgln(" - HeapBlock empty @ {}: cell_size={}", block, block->cell_size());
#endif
allocator_for_size(block->cell_size()).block_did_become_empty({}, *block);
}
for (auto* block : full_blocks_that_became_usable) {
#ifdef HEAP_DEBUG
dbgln(" - HeapBlock usable again @ {}: cell_size={}", block, block->cell_size());
#endif
allocator_for_size(block->cell_size()).block_did_become_usable({}, *block);
}
#ifdef HEAP_DEBUG
for_each_block([&](auto& block) {
dbgln(" > Live HeapBlock @ {}: cell_size={}", &block, block.cell_size());
return IterationDecision::Continue;
});
#endif
int time_spent = measurement_timer.elapsed();
if (print_report) {
size_t live_block_count = 0;
for_each_block([&](auto&) {
++live_block_count;
return IterationDecision::Continue;
});
dbgln("Garbage collection report");
dbgln("=============================================");
dbgln(" Time spent: {} ms", time_spent);
dbgln(" Live cells: {} ({} bytes)", live_cells, live_cell_bytes);
dbgln("Collected cells: {} ({} bytes)", collected_cells, collected_cell_bytes);
dbgln(" Live blocks: {} ({} bytes)", live_block_count, live_block_count * HeapBlock::block_size);
dbgln(" Freed blocks: {} ({} bytes)", empty_blocks.size(), empty_blocks.size() * HeapBlock::block_size);
dbgln("=============================================");
}
}
void Heap::did_create_handle(Badge<HandleImpl>, HandleImpl& impl)
{
ASSERT(!m_handles.contains(&impl));
m_handles.set(&impl);
}
void Heap::did_destroy_handle(Badge<HandleImpl>, HandleImpl& impl)
{
ASSERT(m_handles.contains(&impl));
m_handles.remove(&impl);
}
void Heap::did_create_marked_value_list(Badge<MarkedValueList>, MarkedValueList& list)
{
ASSERT(!m_marked_value_lists.contains(&list));
m_marked_value_lists.set(&list);
}
void Heap::did_destroy_marked_value_list(Badge<MarkedValueList>, MarkedValueList& list)
{
ASSERT(m_marked_value_lists.contains(&list));
m_marked_value_lists.remove(&list);
}
void Heap::defer_gc(Badge<DeferGC>)
{
++m_gc_deferrals;
}
void Heap::undefer_gc(Badge<DeferGC>)
{
ASSERT(m_gc_deferrals > 0);
--m_gc_deferrals;
if (!m_gc_deferrals) {
if (m_should_gc_when_deferral_ends)
collect_garbage();
m_should_gc_when_deferral_ends = false;
}
}
}

View file

@ -0,0 +1,132 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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/HashTable.h>
#include <AK/Noncopyable.h>
#include <AK/NonnullOwnPtr.h>
#include <AK/Types.h>
#include <AK/Vector.h>
#include <LibCore/Forward.h>
#include <LibJS/Forward.h>
#include <LibJS/Heap/Allocator.h>
#include <LibJS/Heap/Handle.h>
#include <LibJS/Runtime/Cell.h>
#include <LibJS/Runtime/Object.h>
namespace JS {
class Heap {
AK_MAKE_NONCOPYABLE(Heap);
AK_MAKE_NONMOVABLE(Heap);
public:
explicit Heap(VM&);
~Heap();
template<typename T, typename... Args>
T* allocate_without_global_object(Args&&... args)
{
auto* memory = allocate_cell(sizeof(T));
new (memory) T(forward<Args>(args)...);
return static_cast<T*>(memory);
}
template<typename T, typename... Args>
T* allocate(GlobalObject& global_object, Args&&... args)
{
auto* memory = allocate_cell(sizeof(T));
new (memory) T(forward<Args>(args)...);
auto* cell = static_cast<T*>(memory);
constexpr bool is_object = IsBaseOf<Object, T>::value;
if constexpr (is_object)
static_cast<Object*>(cell)->disable_transitions();
cell->initialize(global_object);
if constexpr (is_object)
static_cast<Object*>(cell)->enable_transitions();
return cell;
}
enum class CollectionType {
CollectGarbage,
CollectEverything,
};
void collect_garbage(CollectionType = CollectionType::CollectGarbage, bool print_report = false);
VM& vm() { return m_vm; }
bool should_collect_on_every_allocation() const { return m_should_collect_on_every_allocation; }
void set_should_collect_on_every_allocation(bool b) { m_should_collect_on_every_allocation = b; }
void did_create_handle(Badge<HandleImpl>, HandleImpl&);
void did_destroy_handle(Badge<HandleImpl>, HandleImpl&);
void did_create_marked_value_list(Badge<MarkedValueList>, MarkedValueList&);
void did_destroy_marked_value_list(Badge<MarkedValueList>, MarkedValueList&);
void defer_gc(Badge<DeferGC>);
void undefer_gc(Badge<DeferGC>);
private:
Cell* allocate_cell(size_t);
void gather_roots(HashTable<Cell*>&);
void gather_conservative_roots(HashTable<Cell*>&);
void mark_live_cells(const HashTable<Cell*>& live_cells);
void sweep_dead_cells(bool print_report, const Core::ElapsedTimer&);
Allocator& allocator_for_size(size_t);
template<typename Callback>
void for_each_block(Callback callback)
{
for (auto& allocator : m_allocators) {
if (allocator->for_each_block(callback) == IterationDecision::Break)
return;
}
}
size_t m_max_allocations_between_gc { 10000 };
size_t m_allocations_since_last_gc { false };
bool m_should_collect_on_every_allocation { false };
VM& m_vm;
Vector<NonnullOwnPtr<Allocator>> m_allocators;
HashTable<HandleImpl*> m_handles;
HashTable<MarkedValueList*> m_marked_value_lists;
size_t m_gc_deferrals { 0 };
bool m_should_gc_when_deferral_ends { false };
bool m_collecting_garbage { false };
};
}

View file

@ -0,0 +1,87 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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/Assertions.h>
#include <AK/NonnullOwnPtr.h>
#include <AK/kmalloc.h>
#include <LibJS/Heap/HeapBlock.h>
#include <stdio.h>
#include <sys/mman.h>
namespace JS {
NonnullOwnPtr<HeapBlock> HeapBlock::create_with_cell_size(Heap& heap, size_t cell_size)
{
char name[64];
snprintf(name, sizeof(name), "LibJS: HeapBlock(%zu)", cell_size);
#ifdef __serenity__
auto* block = (HeapBlock*)serenity_mmap(nullptr, block_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0, block_size, name);
#else
auto* block = (HeapBlock*)aligned_alloc(block_size, block_size);
#endif
ASSERT(block != MAP_FAILED);
new (block) HeapBlock(heap, cell_size);
return NonnullOwnPtr<HeapBlock>(NonnullOwnPtr<HeapBlock>::Adopt, *block);
}
void HeapBlock::operator delete(void* ptr)
{
#ifdef __serenity__
int rc = munmap(ptr, block_size);
ASSERT(rc == 0);
#else
free(ptr);
#endif
}
HeapBlock::HeapBlock(Heap& heap, size_t cell_size)
: m_heap(heap)
, m_cell_size(cell_size)
{
ASSERT(cell_size >= sizeof(FreelistEntry));
FreelistEntry* next = nullptr;
for (ssize_t i = cell_count() - 1; i >= 0; i--) {
auto* freelist_entry = init_freelist_entry(i);
freelist_entry->set_live(false);
freelist_entry->next = next;
next = freelist_entry;
}
m_freelist = next;
}
void HeapBlock::deallocate(Cell* cell)
{
ASSERT(cell->is_live());
ASSERT(!cell->is_marked());
cell->~Cell();
auto* freelist_entry = new (cell) FreelistEntry();
freelist_entry->set_live(false);
freelist_entry->next = m_freelist;
m_freelist = freelist_entry;
}
}

View file

@ -0,0 +1,110 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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/IntrusiveList.h>
#include <AK/Types.h>
#include <LibJS/Forward.h>
#include <LibJS/Runtime/Cell.h>
namespace JS {
class HeapBlock {
AK_MAKE_NONCOPYABLE(HeapBlock);
AK_MAKE_NONMOVABLE(HeapBlock);
public:
static constexpr size_t block_size = 16 * KiB;
static NonnullOwnPtr<HeapBlock> create_with_cell_size(Heap&, size_t);
void operator delete(void*);
size_t cell_size() const { return m_cell_size; }
size_t cell_count() const { return (block_size - sizeof(HeapBlock)) / m_cell_size; }
bool is_full() const { return !m_freelist; }
ALWAYS_INLINE Cell* allocate()
{
if (!m_freelist)
return nullptr;
return exchange(m_freelist, m_freelist->next);
}
void deallocate(Cell*);
template<typename Callback>
void for_each_cell(Callback callback)
{
for (size_t i = 0; i < cell_count(); ++i)
callback(cell(i));
}
Heap& heap() { return m_heap; }
static HeapBlock* from_cell(const Cell* cell)
{
return reinterpret_cast<HeapBlock*>((FlatPtr)cell & ~(block_size - 1));
}
Cell* cell_from_possible_pointer(FlatPtr pointer)
{
if (pointer < reinterpret_cast<FlatPtr>(m_storage))
return nullptr;
size_t cell_index = (pointer - reinterpret_cast<FlatPtr>(m_storage)) / m_cell_size;
if (cell_index >= cell_count())
return nullptr;
return cell(cell_index);
}
IntrusiveListNode m_list_node;
private:
HeapBlock(Heap&, size_t cell_size);
struct FreelistEntry final : public Cell {
FreelistEntry* next { nullptr };
virtual const char* class_name() const override { return "FreelistEntry"; }
};
Cell* cell(size_t index)
{
return reinterpret_cast<Cell*>(&m_storage[index * cell_size()]);
}
FreelistEntry* init_freelist_entry(size_t index)
{
return new (&m_storage[index * cell_size()]) FreelistEntry();
}
Heap& m_heap;
size_t m_cell_size { 0 };
FreelistEntry* m_freelist { nullptr };
alignas(Cell) u8 m_storage[];
};
}

View file

@ -0,0 +1,197 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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/Badge.h>
#include <AK/StringBuilder.h>
#include <LibJS/AST.h>
#include <LibJS/Interpreter.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/LexicalEnvironment.h>
#include <LibJS/Runtime/MarkedValueList.h>
#include <LibJS/Runtime/NativeFunction.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Reference.h>
#include <LibJS/Runtime/ScriptFunction.h>
#include <LibJS/Runtime/Shape.h>
#include <LibJS/Runtime/SymbolObject.h>
#include <LibJS/Runtime/Value.h>
namespace JS {
NonnullOwnPtr<Interpreter> Interpreter::create_with_existing_global_object(GlobalObject& global_object)
{
DeferGC defer_gc(global_object.heap());
auto interpreter = adopt_own(*new Interpreter(global_object.vm()));
interpreter->m_global_object = make_handle(static_cast<Object*>(&global_object));
return interpreter;
}
Interpreter::Interpreter(VM& vm)
: m_vm(vm)
{
}
Interpreter::~Interpreter()
{
}
Value Interpreter::run(GlobalObject& global_object, const Program& program)
{
auto& vm = this->vm();
ASSERT(!vm.exception());
VM::InterpreterExecutionScope scope(*this);
CallFrame global_call_frame;
global_call_frame.this_value = &global_object;
static FlyString global_execution_context_name = "(global execution context)";
global_call_frame.function_name = global_execution_context_name;
global_call_frame.scope = &global_object;
ASSERT(!vm.exception());
global_call_frame.is_strict_mode = program.is_strict_mode();
vm.push_call_frame(global_call_frame, global_object);
ASSERT(!vm.exception());
auto result = program.execute(*this, global_object);
vm.pop_call_frame();
return result;
}
GlobalObject& Interpreter::global_object()
{
return static_cast<GlobalObject&>(*m_global_object.cell());
}
const GlobalObject& Interpreter::global_object() const
{
return static_cast<const GlobalObject&>(*m_global_object.cell());
}
void Interpreter::enter_scope(const ScopeNode& scope_node, ScopeType scope_type, GlobalObject& global_object)
{
for (auto& declaration : scope_node.functions()) {
auto* function = ScriptFunction::create(global_object, declaration.name(), declaration.body(), declaration.parameters(), declaration.function_length(), current_scope(), declaration.is_strict_mode());
vm().set_variable(declaration.name(), function, global_object);
}
if (scope_type == ScopeType::Function) {
push_scope({ scope_type, scope_node, false });
return;
}
HashMap<FlyString, Variable> scope_variables_with_declaration_kind;
scope_variables_with_declaration_kind.ensure_capacity(16);
for (auto& declaration : scope_node.variables()) {
for (auto& declarator : declaration.declarations()) {
if (is<Program>(scope_node)) {
global_object.put(declarator.id().string(), js_undefined());
if (exception())
return;
} else {
scope_variables_with_declaration_kind.set(declarator.id().string(), { js_undefined(), declaration.declaration_kind() });
}
}
}
bool pushed_lexical_environment = false;
if (!scope_variables_with_declaration_kind.is_empty()) {
auto* block_lexical_environment = heap().allocate<LexicalEnvironment>(global_object, move(scope_variables_with_declaration_kind), current_scope());
vm().call_frame().scope = block_lexical_environment;
pushed_lexical_environment = true;
}
push_scope({ scope_type, scope_node, pushed_lexical_environment });
}
void Interpreter::exit_scope(const ScopeNode& scope_node)
{
while (!m_scope_stack.is_empty()) {
auto popped_scope = m_scope_stack.take_last();
if (popped_scope.pushed_environment)
vm().call_frame().scope = vm().call_frame().scope->parent();
if (popped_scope.scope_node.ptr() == &scope_node)
break;
}
// If we unwind all the way, just reset m_unwind_until so that future "return" doesn't break.
if (m_scope_stack.is_empty())
vm().unwind(ScopeType::None);
}
void Interpreter::enter_node(const ASTNode& node)
{
vm().push_ast_node(node);
}
void Interpreter::exit_node(const ASTNode&)
{
vm().pop_ast_node();
}
void Interpreter::push_scope(ScopeFrame frame)
{
m_scope_stack.append(move(frame));
}
Value Interpreter::execute_statement(GlobalObject& global_object, const Statement& statement, ScopeType scope_type)
{
if (!is<ScopeNode>(statement))
return statement.execute(*this, global_object);
auto& block = static_cast<const ScopeNode&>(statement);
enter_scope(block, scope_type, global_object);
if (block.children().is_empty())
vm().set_last_value({}, js_undefined());
for (auto& node : block.children()) {
vm().set_last_value({}, node.execute(*this, global_object));
if (vm().should_unwind()) {
if (!block.label().is_null() && vm().should_unwind_until(ScopeType::Breakable, block.label()))
vm().stop_unwind();
break;
}
}
bool did_return = vm().unwind_until() == ScopeType::Function;
if (vm().unwind_until() == scope_type)
vm().unwind(ScopeType::None);
exit_scope(block);
return did_return ? vm().last_value() : js_undefined();
}
LexicalEnvironment* Interpreter::current_environment()
{
ASSERT(is<LexicalEnvironment>(vm().call_frame().scope));
return static_cast<LexicalEnvironment*>(vm().call_frame().scope);
}
}

View file

@ -0,0 +1,97 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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/FlyString.h>
#include <AK/HashMap.h>
#include <AK/String.h>
#include <AK/Vector.h>
#include <AK/Weakable.h>
#include <LibJS/AST.h>
#include <LibJS/Forward.h>
#include <LibJS/Heap/DeferGC.h>
#include <LibJS/Heap/Heap.h>
#include <LibJS/Runtime/ErrorTypes.h>
#include <LibJS/Runtime/Exception.h>
#include <LibJS/Runtime/LexicalEnvironment.h>
#include <LibJS/Runtime/MarkedValueList.h>
#include <LibJS/Runtime/VM.h>
#include <LibJS/Runtime/Value.h>
namespace JS {
class Interpreter : public Weakable<Interpreter> {
public:
template<typename GlobalObjectType, typename... Args>
static NonnullOwnPtr<Interpreter> create(VM& vm, Args&&... args)
{
DeferGC defer_gc(vm.heap());
auto interpreter = adopt_own(*new Interpreter(vm));
VM::InterpreterExecutionScope scope(*interpreter);
interpreter->m_global_object = make_handle(static_cast<Object*>(interpreter->heap().allocate_without_global_object<GlobalObjectType>(forward<Args>(args)...)));
static_cast<GlobalObjectType*>(interpreter->m_global_object.cell())->initialize();
return interpreter;
}
static NonnullOwnPtr<Interpreter> create_with_existing_global_object(GlobalObject&);
~Interpreter();
Value run(GlobalObject&, const Program&);
GlobalObject& global_object();
const GlobalObject& global_object() const;
VM& vm() { return *m_vm; }
const VM& vm() const { return *m_vm; }
Heap& heap() { return vm().heap(); }
Exception* exception() { return vm().exception(); }
ScopeObject* current_scope() { return vm().current_scope(); }
LexicalEnvironment* current_environment();
void enter_scope(const ScopeNode&, ScopeType, GlobalObject&);
void exit_scope(const ScopeNode&);
void enter_node(const ASTNode&);
void exit_node(const ASTNode&);
Value execute_statement(GlobalObject&, const Statement&, ScopeType = ScopeType::Block);
private:
explicit Interpreter(VM&);
void push_scope(ScopeFrame frame);
Vector<ScopeFrame> m_scope_stack;
NonnullRefPtr<VM> m_vm;
Handle<Object> m_global_object;
};
}

View file

@ -0,0 +1,655 @@
/*
* Copyright (c) 2020, Stephan Unverwerth <s.unverwerth@gmx.de>
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* 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 "Lexer.h"
#include <AK/HashMap.h>
#include <AK/StringBuilder.h>
#include <ctype.h>
#include <stdio.h>
//#define LEXER_DEBUG
namespace JS {
HashMap<String, TokenType> Lexer::s_keywords;
HashMap<String, TokenType> Lexer::s_three_char_tokens;
HashMap<String, TokenType> Lexer::s_two_char_tokens;
HashMap<char, TokenType> Lexer::s_single_char_tokens;
Lexer::Lexer(StringView source)
: m_source(source)
, m_current_token(TokenType::Eof, {}, StringView(nullptr), StringView(nullptr), 0, 0)
{
if (s_keywords.is_empty()) {
s_keywords.set("await", TokenType::Await);
s_keywords.set("break", TokenType::Break);
s_keywords.set("case", TokenType::Case);
s_keywords.set("catch", TokenType::Catch);
s_keywords.set("class", TokenType::Class);
s_keywords.set("const", TokenType::Const);
s_keywords.set("continue", TokenType::Continue);
s_keywords.set("debugger", TokenType::Debugger);
s_keywords.set("default", TokenType::Default);
s_keywords.set("delete", TokenType::Delete);
s_keywords.set("do", TokenType::Do);
s_keywords.set("else", TokenType::Else);
s_keywords.set("enum", TokenType::Enum);
s_keywords.set("export", TokenType::Export);
s_keywords.set("extends", TokenType::Extends);
s_keywords.set("false", TokenType::BoolLiteral);
s_keywords.set("finally", TokenType::Finally);
s_keywords.set("for", TokenType::For);
s_keywords.set("function", TokenType::Function);
s_keywords.set("if", TokenType::If);
s_keywords.set("import", TokenType::Import);
s_keywords.set("in", TokenType::In);
s_keywords.set("instanceof", TokenType::Instanceof);
s_keywords.set("let", TokenType::Let);
s_keywords.set("new", TokenType::New);
s_keywords.set("null", TokenType::NullLiteral);
s_keywords.set("return", TokenType::Return);
s_keywords.set("super", TokenType::Super);
s_keywords.set("switch", TokenType::Switch);
s_keywords.set("this", TokenType::This);
s_keywords.set("throw", TokenType::Throw);
s_keywords.set("true", TokenType::BoolLiteral);
s_keywords.set("try", TokenType::Try);
s_keywords.set("typeof", TokenType::Typeof);
s_keywords.set("var", TokenType::Var);
s_keywords.set("void", TokenType::Void);
s_keywords.set("while", TokenType::While);
s_keywords.set("with", TokenType::With);
s_keywords.set("yield", TokenType::Yield);
}
if (s_three_char_tokens.is_empty()) {
s_three_char_tokens.set("===", TokenType::EqualsEqualsEquals);
s_three_char_tokens.set("!==", TokenType::ExclamationMarkEqualsEquals);
s_three_char_tokens.set("**=", TokenType::DoubleAsteriskEquals);
s_three_char_tokens.set("<<=", TokenType::ShiftLeftEquals);
s_three_char_tokens.set(">>=", TokenType::ShiftRightEquals);
s_three_char_tokens.set("&&=", TokenType::DoubleAmpersandEquals);
s_three_char_tokens.set("||=", TokenType::DoublePipeEquals);
s_three_char_tokens.set("\?\?=", TokenType::DoubleQuestionMarkEquals);
s_three_char_tokens.set(">>>", TokenType::UnsignedShiftRight);
s_three_char_tokens.set("...", TokenType::TripleDot);
}
if (s_two_char_tokens.is_empty()) {
s_two_char_tokens.set("=>", TokenType::Arrow);
s_two_char_tokens.set("+=", TokenType::PlusEquals);
s_two_char_tokens.set("-=", TokenType::MinusEquals);
s_two_char_tokens.set("*=", TokenType::AsteriskEquals);
s_two_char_tokens.set("/=", TokenType::SlashEquals);
s_two_char_tokens.set("%=", TokenType::PercentEquals);
s_two_char_tokens.set("&=", TokenType::AmpersandEquals);
s_two_char_tokens.set("|=", TokenType::PipeEquals);
s_two_char_tokens.set("^=", TokenType::CaretEquals);
s_two_char_tokens.set("&&", TokenType::DoubleAmpersand);
s_two_char_tokens.set("||", TokenType::DoublePipe);
s_two_char_tokens.set("??", TokenType::DoubleQuestionMark);
s_two_char_tokens.set("**", TokenType::DoubleAsterisk);
s_two_char_tokens.set("==", TokenType::EqualsEquals);
s_two_char_tokens.set("<=", TokenType::LessThanEquals);
s_two_char_tokens.set(">=", TokenType::GreaterThanEquals);
s_two_char_tokens.set("!=", TokenType::ExclamationMarkEquals);
s_two_char_tokens.set("--", TokenType::MinusMinus);
s_two_char_tokens.set("++", TokenType::PlusPlus);
s_two_char_tokens.set("<<", TokenType::ShiftLeft);
s_two_char_tokens.set(">>", TokenType::ShiftRight);
s_two_char_tokens.set("?.", TokenType::QuestionMarkPeriod);
}
if (s_single_char_tokens.is_empty()) {
s_single_char_tokens.set('&', TokenType::Ampersand);
s_single_char_tokens.set('*', TokenType::Asterisk);
s_single_char_tokens.set('[', TokenType::BracketOpen);
s_single_char_tokens.set(']', TokenType::BracketClose);
s_single_char_tokens.set('^', TokenType::Caret);
s_single_char_tokens.set(':', TokenType::Colon);
s_single_char_tokens.set(',', TokenType::Comma);
s_single_char_tokens.set('{', TokenType::CurlyOpen);
s_single_char_tokens.set('}', TokenType::CurlyClose);
s_single_char_tokens.set('=', TokenType::Equals);
s_single_char_tokens.set('!', TokenType::ExclamationMark);
s_single_char_tokens.set('-', TokenType::Minus);
s_single_char_tokens.set('(', TokenType::ParenOpen);
s_single_char_tokens.set(')', TokenType::ParenClose);
s_single_char_tokens.set('%', TokenType::Percent);
s_single_char_tokens.set('.', TokenType::Period);
s_single_char_tokens.set('|', TokenType::Pipe);
s_single_char_tokens.set('+', TokenType::Plus);
s_single_char_tokens.set('?', TokenType::QuestionMark);
s_single_char_tokens.set(';', TokenType::Semicolon);
s_single_char_tokens.set('/', TokenType::Slash);
s_single_char_tokens.set('~', TokenType::Tilde);
s_single_char_tokens.set('<', TokenType::LessThan);
s_single_char_tokens.set('>', TokenType::GreaterThan);
}
consume();
}
void Lexer::consume()
{
auto did_reach_eof = [this] {
if (m_position != m_source.length())
return false;
m_position++;
m_line_column++;
m_current_char = EOF;
return true;
};
if (m_position > m_source.length())
return;
if (did_reach_eof())
return;
if (is_line_terminator()) {
#ifdef LEXER_DEBUG
String type;
if (m_current_char == '\n')
type = "LINE FEED";
else if (m_current_char == '\r')
type = "CARRIAGE RETURN";
else if (m_source[m_position + 1] == (char)0xa8)
type = "LINE SEPARATOR";
else
type = "PARAGRAPH SEPARATOR";
dbgln("Found a line terminator: {}", type);
#endif
// This is a three-char line terminator, we need to increase m_position some more.
// We might reach EOF and need to check again.
if (m_current_char != '\n' && m_current_char != '\r') {
m_position += 2;
if (did_reach_eof())
return;
}
// If the previous character is \r and the current one \n we already updated line number
// and column - don't do it again. From https://tc39.es/ecma262/#sec-line-terminators:
// The sequence <CR><LF> is commonly used as a line terminator.
// It should be considered a single SourceCharacter for the purpose of reporting line numbers.
auto second_char_of_crlf = m_position > 1 && m_source[m_position - 2] == '\r' && m_current_char == '\n';
if (!second_char_of_crlf) {
m_line_number++;
m_line_column = 1;
#ifdef LEXER_DEBUG
dbgln("Incremented line number, now at: line {}, column 1", m_line_number);
} else {
dbgln("Previous was CR, this is LF - not incrementing line number again.");
#endif
}
} else {
m_line_column++;
}
m_current_char = m_source[m_position++];
}
bool Lexer::consume_exponent()
{
consume();
if (m_current_char == '-' || m_current_char == '+')
consume();
if (!isdigit(m_current_char))
return false;
while (isdigit(m_current_char)) {
consume();
}
return true;
}
bool Lexer::consume_octal_number()
{
consume();
if (!(m_current_char >= '0' && m_current_char <= '7'))
return false;
while (m_current_char >= '0' && m_current_char <= '7') {
consume();
}
return true;
}
bool Lexer::consume_hexadecimal_number()
{
consume();
if (!isxdigit(m_current_char))
return false;
while (isxdigit(m_current_char))
consume();
return true;
}
bool Lexer::consume_binary_number()
{
consume();
if (!(m_current_char == '0' || m_current_char == '1'))
return false;
while (m_current_char == '0' || m_current_char == '1')
consume();
return true;
}
bool Lexer::match(char a, char b) const
{
if (m_position >= m_source.length())
return false;
return m_current_char == a
&& m_source[m_position] == b;
}
bool Lexer::match(char a, char b, char c) const
{
if (m_position + 1 >= m_source.length())
return false;
return m_current_char == a
&& m_source[m_position] == b
&& m_source[m_position + 1] == c;
}
bool Lexer::match(char a, char b, char c, char d) const
{
if (m_position + 2 >= m_source.length())
return false;
return m_current_char == a
&& m_source[m_position] == b
&& m_source[m_position + 1] == c
&& m_source[m_position + 2] == d;
}
bool Lexer::is_eof() const
{
return m_current_char == EOF;
}
bool Lexer::is_line_terminator() const
{
if (m_current_char == '\n' || m_current_char == '\r')
return true;
if (m_position > 0 && m_position + 1 < m_source.length()) {
auto three_chars_view = m_source.substring_view(m_position - 1, 3);
return (three_chars_view == LINE_SEPARATOR) || (three_chars_view == PARAGRAPH_SEPARATOR);
}
return false;
}
bool Lexer::is_identifier_start() const
{
return isalpha(m_current_char) || m_current_char == '_' || m_current_char == '$';
}
bool Lexer::is_identifier_middle() const
{
return is_identifier_start() || isdigit(m_current_char);
}
bool Lexer::is_line_comment_start(bool line_has_token_yet) const
{
return match('/', '/')
|| match('<', '!', '-', '-')
// "-->" is considered a line comment start if the current line is only whitespace and/or
// other block comment(s); or in other words: the current line does not have a token or
// ongoing line comment yet
|| (match('-', '-', '>') && !line_has_token_yet);
}
bool Lexer::is_block_comment_start() const
{
return match('/', '*');
}
bool Lexer::is_block_comment_end() const
{
return match('*', '/');
}
bool Lexer::is_numeric_literal_start() const
{
return isdigit(m_current_char) || (m_current_char == '.' && m_position < m_source.length() && isdigit(m_source[m_position]));
}
bool Lexer::slash_means_division() const
{
auto type = m_current_token.type();
return type == TokenType::BigIntLiteral
|| type == TokenType::BoolLiteral
|| type == TokenType::BracketClose
|| type == TokenType::CurlyClose
|| type == TokenType::Identifier
|| type == TokenType::NullLiteral
|| type == TokenType::NumericLiteral
|| type == TokenType::ParenClose
|| type == TokenType::RegexLiteral
|| type == TokenType::StringLiteral
|| type == TokenType::TemplateLiteralEnd
|| type == TokenType::This;
}
Token Lexer::next()
{
size_t trivia_start = m_position;
auto in_template = !m_template_states.is_empty();
bool line_has_token_yet = m_line_column > 1;
bool unterminated_comment = false;
if (!in_template || m_template_states.last().in_expr) {
// consume whitespace and comments
while (true) {
if (is_line_terminator()) {
line_has_token_yet = false;
do {
consume();
} while (is_line_terminator());
} else if (isspace(m_current_char)) {
do {
consume();
} while (isspace(m_current_char));
} else if (is_line_comment_start(line_has_token_yet)) {
consume();
do {
consume();
} while (!is_eof() && !is_line_terminator());
} else if (is_block_comment_start()) {
consume();
do {
consume();
} while (!is_eof() && !is_block_comment_end());
if (is_eof())
unterminated_comment = true;
consume(); // consume *
if (is_eof())
unterminated_comment = true;
consume(); // consume /
} else {
break;
}
}
}
size_t value_start = m_position;
size_t value_start_line_number = m_line_number;
size_t value_start_column_number = m_line_column;
auto token_type = TokenType::Invalid;
// This is being used to communicate info about invalid tokens to the parser, which then
// can turn that into more specific error messages - instead of us having to make up a
// bunch of Invalid* tokens (bad numeric literals, unterminated comments etc.)
String token_message;
if (m_current_token.type() == TokenType::RegexLiteral && !is_eof() && isalpha(m_current_char)) {
token_type = TokenType::RegexFlags;
while (!is_eof() && isalpha(m_current_char))
consume();
} else if (m_current_char == '`') {
consume();
if (!in_template) {
token_type = TokenType::TemplateLiteralStart;
m_template_states.append({ false, 0 });
} else {
if (m_template_states.last().in_expr) {
m_template_states.append({ false, 0 });
token_type = TokenType::TemplateLiteralStart;
} else {
m_template_states.take_last();
token_type = TokenType::TemplateLiteralEnd;
}
}
} else if (in_template && m_template_states.last().in_expr && m_template_states.last().open_bracket_count == 0 && m_current_char == '}') {
consume();
token_type = TokenType::TemplateLiteralExprEnd;
m_template_states.last().in_expr = false;
} else if (in_template && !m_template_states.last().in_expr) {
if (is_eof()) {
token_type = TokenType::UnterminatedTemplateLiteral;
m_template_states.take_last();
} else if (match('$', '{')) {
token_type = TokenType::TemplateLiteralExprStart;
consume();
consume();
m_template_states.last().in_expr = true;
} else {
while (!match('$', '{') && m_current_char != '`' && !is_eof()) {
if (match('\\', '$') || match('\\', '`'))
consume();
consume();
}
if (is_eof() && !m_template_states.is_empty())
token_type = TokenType::UnterminatedTemplateLiteral;
else
token_type = TokenType::TemplateLiteralString;
}
} else if (is_identifier_start()) {
// identifier or keyword
do {
consume();
} while (is_identifier_middle());
StringView value = m_source.substring_view(value_start - 1, m_position - value_start);
auto it = s_keywords.find(value.hash(), [&](auto& entry) { return entry.key == value; });
if (it == s_keywords.end()) {
token_type = TokenType::Identifier;
} else {
token_type = it->value;
}
} else if (is_numeric_literal_start()) {
token_type = TokenType::NumericLiteral;
bool is_invalid_numeric_literal = false;
if (m_current_char == '0') {
consume();
if (m_current_char == '.') {
// decimal
consume();
while (isdigit(m_current_char))
consume();
if (m_current_char == 'e' || m_current_char == 'E')
is_invalid_numeric_literal = !consume_exponent();
} else if (m_current_char == 'e' || m_current_char == 'E') {
is_invalid_numeric_literal = !consume_exponent();
} else if (m_current_char == 'o' || m_current_char == 'O') {
// octal
is_invalid_numeric_literal = !consume_octal_number();
} else if (m_current_char == 'b' || m_current_char == 'B') {
// binary
is_invalid_numeric_literal = !consume_binary_number();
} else if (m_current_char == 'x' || m_current_char == 'X') {
// hexadecimal
is_invalid_numeric_literal = !consume_hexadecimal_number();
} else if (m_current_char == 'n') {
consume();
token_type = TokenType::BigIntLiteral;
} else if (isdigit(m_current_char)) {
// octal without '0o' prefix. Forbidden in 'strict mode'
do {
consume();
} while (isdigit(m_current_char));
}
} else {
// 1...9 or period
while (isdigit(m_current_char))
consume();
if (m_current_char == 'n') {
consume();
token_type = TokenType::BigIntLiteral;
} else {
if (m_current_char == '.') {
consume();
while (isdigit(m_current_char))
consume();
}
if (m_current_char == 'e' || m_current_char == 'E')
is_invalid_numeric_literal = !consume_exponent();
}
}
if (is_invalid_numeric_literal) {
token_type = TokenType::Invalid;
token_message = "Invalid numeric literal";
}
} else if (m_current_char == '"' || m_current_char == '\'') {
char stop_char = m_current_char;
consume();
// Note: LS/PS line terminators are allowed in string literals.
while (m_current_char != stop_char && m_current_char != '\r' && m_current_char != '\n' && !is_eof()) {
if (m_current_char == '\\') {
consume();
}
consume();
}
if (m_current_char != stop_char) {
token_type = TokenType::UnterminatedStringLiteral;
} else {
consume();
token_type = TokenType::StringLiteral;
}
} else if (m_current_char == '/' && !slash_means_division()) {
consume();
token_type = TokenType::RegexLiteral;
while (!is_eof()) {
if (m_current_char == '[') {
m_regex_is_in_character_class = true;
} else if (m_current_char == ']') {
m_regex_is_in_character_class = false;
} else if (!m_regex_is_in_character_class && m_current_char == '/') {
break;
}
if (match('\\', '/') || match('\\', '[') || match('\\', '\\') || (m_regex_is_in_character_class && match('\\', ']')))
consume();
consume();
}
if (is_eof()) {
token_type = TokenType::UnterminatedRegexLiteral;
} else {
consume();
}
} else if (m_current_char == EOF) {
if (unterminated_comment) {
token_type = TokenType::Invalid;
token_message = "Unterminated multi-line comment";
} else {
token_type = TokenType::Eof;
}
} else {
// There is only one four-char operator: >>>=
bool found_four_char_token = false;
if (match('>', '>', '>', '=')) {
found_four_char_token = true;
consume();
consume();
consume();
consume();
token_type = TokenType::UnsignedShiftRightEquals;
}
bool found_three_char_token = false;
if (!found_four_char_token && m_position + 1 < m_source.length()) {
auto three_chars_view = m_source.substring_view(m_position - 1, 3);
auto it = s_three_char_tokens.find(three_chars_view.hash(), [&](auto& entry) { return entry.key == three_chars_view; });
if (it != s_three_char_tokens.end()) {
found_three_char_token = true;
consume();
consume();
consume();
token_type = it->value;
}
}
bool found_two_char_token = false;
if (!found_four_char_token && !found_three_char_token && m_position < m_source.length()) {
auto two_chars_view = m_source.substring_view(m_position - 1, 2);
auto it = s_two_char_tokens.find(two_chars_view.hash(), [&](auto& entry) { return entry.key == two_chars_view; });
if (it != s_two_char_tokens.end()) {
// OptionalChainingPunctuator :: ?. [lookahead ∉ DecimalDigit]
if (!(it->value == TokenType::QuestionMarkPeriod && m_position + 1 < m_source.length() && isdigit(m_source[m_position + 1]))) {
found_two_char_token = true;
consume();
consume();
token_type = it->value;
}
}
}
bool found_one_char_token = false;
if (!found_four_char_token && !found_three_char_token && !found_two_char_token) {
auto it = s_single_char_tokens.find(m_current_char);
if (it != s_single_char_tokens.end()) {
found_one_char_token = true;
consume();
token_type = it->value;
}
}
if (!found_four_char_token && !found_three_char_token && !found_two_char_token && !found_one_char_token) {
consume();
token_type = TokenType::Invalid;
}
}
if (!m_template_states.is_empty() && m_template_states.last().in_expr) {
if (token_type == TokenType::CurlyOpen) {
m_template_states.last().open_bracket_count++;
} else if (token_type == TokenType::CurlyClose) {
m_template_states.last().open_bracket_count--;
}
}
m_current_token = Token(
token_type,
token_message,
m_source.substring_view(trivia_start - 1, value_start - trivia_start),
m_source.substring_view(value_start - 1, m_position - value_start),
value_start_line_number,
value_start_column_number);
#ifdef LEXER_DEBUG
dbgln("------------------------------");
dbgln("Token: {}", m_current_token.name());
dbgln("Trivia: _{}_", m_current_token.trivia());
dbgln("Value: _{}_", m_current_token.value());
dbgln("Line: {}, Column: {}", m_current_token.line_number(), m_current_token.line_column());
dbgln("------------------------------");
#endif
return m_current_token;
}
}

View file

@ -0,0 +1,85 @@
/*
* Copyright (c) 2020, Stephan Unverwerth <s.unverwerth@gmx.de>
* 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 "Token.h"
#include <AK/HashMap.h>
#include <AK/String.h>
#include <AK/StringView.h>
namespace JS {
class Lexer {
public:
explicit Lexer(StringView source);
Token next();
const StringView& source() const { return m_source; };
private:
void consume();
bool consume_exponent();
bool consume_octal_number();
bool consume_hexadecimal_number();
bool consume_binary_number();
bool is_eof() const;
bool is_line_terminator() const;
bool is_identifier_start() const;
bool is_identifier_middle() const;
bool is_line_comment_start(bool line_has_token_yet) const;
bool is_block_comment_start() const;
bool is_block_comment_end() const;
bool is_numeric_literal_start() const;
bool match(char, char) const;
bool match(char, char, char) const;
bool match(char, char, char, char) const;
bool slash_means_division() const;
StringView m_source;
size_t m_position { 0 };
Token m_current_token;
char m_current_char { 0 };
size_t m_line_number { 1 };
size_t m_line_column { 0 };
bool m_regex_is_in_character_class { false };
struct TemplateState {
bool in_expr;
u8 open_bracket_count;
};
Vector<TemplateState> m_template_states;
static HashMap<String, TokenType> s_keywords;
static HashMap<String, TokenType> s_three_char_tokens;
static HashMap<String, TokenType> s_two_char_tokens;
static HashMap<char, TokenType> s_single_char_tokens;
};
}

View file

@ -0,0 +1,233 @@
/*
* Copyright (c) 2020, Hunter Salyer <thefalsehonesty@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/HashTable.h>
#include <AK/StringBuilder.h>
#include <LibJS/Lexer.h>
#include <LibJS/MarkupGenerator.h>
#include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/Date.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/Object.h>
namespace JS {
String MarkupGenerator::html_from_source(const StringView& source)
{
StringBuilder builder;
auto lexer = Lexer(source);
for (auto token = lexer.next(); token.type() != TokenType::Eof; token = lexer.next()) {
builder.append(token.trivia());
builder.append(wrap_string_in_style(token.value(), style_type_for_token(token)));
}
return builder.to_string();
}
String MarkupGenerator::html_from_value(Value value)
{
StringBuilder output_html;
value_to_html(value, output_html);
return output_html.to_string();
}
void MarkupGenerator::value_to_html(Value value, StringBuilder& output_html, HashTable<Object*> seen_objects)
{
if (value.is_empty()) {
output_html.append("&lt;empty&gt;");
return;
}
if (value.is_object()) {
if (seen_objects.contains(&value.as_object())) {
// FIXME: Maybe we should only do this for circular references,
// not for all reoccurring objects.
output_html.appendff("&lt;already printed Object {:p}&gt;", &value.as_object());
return;
}
seen_objects.set(&value.as_object());
}
if (value.is_array())
return array_to_html(static_cast<const Array&>(value.as_object()), output_html, seen_objects);
if (value.is_object()) {
auto& object = value.as_object();
if (object.is_function())
return function_to_html(object, output_html, seen_objects);
if (is<Date>(object))
return date_to_html(object, output_html, seen_objects);
if (is<Error>(object))
return error_to_html(object, output_html, seen_objects);
return object_to_html(object, output_html, seen_objects);
}
if (value.is_string())
output_html.append(open_style_type(StyleType::String));
else if (value.is_number())
output_html.append(open_style_type(StyleType::Number));
else if (value.is_boolean() || value.is_nullish())
output_html.append(open_style_type(StyleType::KeywordBold));
if (value.is_string())
output_html.append('"');
output_html.append(escape_html_entities(value.to_string_without_side_effects()));
if (value.is_string())
output_html.append('"');
output_html.append("</span>");
}
void MarkupGenerator::array_to_html(const Array& array, StringBuilder& html_output, HashTable<Object*>& seen_objects)
{
html_output.append(wrap_string_in_style("[ ", StyleType::Punctuation));
bool first = true;
for (auto it = array.indexed_properties().begin(false); it != array.indexed_properties().end(); ++it) {
if (!first)
html_output.append(wrap_string_in_style(", ", StyleType::Punctuation));
first = false;
// FIXME: Exception check
value_to_html(it.value_and_attributes(const_cast<Array*>(&array)).value, html_output, seen_objects);
}
html_output.append(wrap_string_in_style(" ]", StyleType::Punctuation));
}
void MarkupGenerator::object_to_html(const Object& object, StringBuilder& html_output, HashTable<Object*>& seen_objects)
{
html_output.append(wrap_string_in_style("{ ", StyleType::Punctuation));
bool first = true;
for (auto& entry : object.indexed_properties()) {
if (!first)
html_output.append(wrap_string_in_style(", ", StyleType::Punctuation));
first = false;
html_output.append(wrap_string_in_style(String::number(entry.index()), StyleType::Number));
html_output.append(wrap_string_in_style(": ", StyleType::Punctuation));
// FIXME: Exception check
value_to_html(entry.value_and_attributes(const_cast<Object*>(&object)).value, html_output, seen_objects);
}
if (!object.indexed_properties().is_empty() && object.shape().property_count())
html_output.append(wrap_string_in_style(", ", StyleType::Punctuation));
size_t index = 0;
for (auto& it : object.shape().property_table_ordered()) {
html_output.append(wrap_string_in_style(String::formatted("\"{}\"", escape_html_entities(it.key.to_display_string())), StyleType::String));
html_output.append(wrap_string_in_style(": ", StyleType::Punctuation));
value_to_html(object.get_direct(it.value.offset), html_output, seen_objects);
if (index != object.shape().property_count() - 1)
html_output.append(wrap_string_in_style(", ", StyleType::Punctuation));
++index;
}
html_output.append(wrap_string_in_style(" }", StyleType::Punctuation));
}
void MarkupGenerator::function_to_html(const Object& function, StringBuilder& html_output, HashTable<Object*>&)
{
html_output.appendff("[{}]", function.class_name());
}
void MarkupGenerator::date_to_html(const Object& date, StringBuilder& html_output, HashTable<Object*>&)
{
html_output.appendff("Date {}", static_cast<const JS::Date&>(date).string());
}
void MarkupGenerator::error_to_html(const Object& object, StringBuilder& html_output, HashTable<Object*>&)
{
auto& error = static_cast<const Error&>(object);
html_output.append(wrap_string_in_style(String::formatted("[{}]", error.name()), StyleType::Invalid));
if (!error.message().is_empty()) {
html_output.appendff(": {}", escape_html_entities(error.message()));
}
}
String MarkupGenerator::style_from_style_type(StyleType type)
{
switch (type) {
case StyleType::Invalid:
return "color: red;";
case StyleType::String:
return "color: -libweb-palette-syntax-string;";
case StyleType::Number:
return "color: -libweb-palette-syntax-number;";
case StyleType::KeywordBold:
return "color: -libweb-palette-syntax-keyword; font-weight: bold;";
case StyleType::Punctuation:
return "color: -libweb-palette-syntax-punctuation;";
case StyleType::Operator:
return "color: -libweb-palette-syntax-operator;";
case StyleType::Keyword:
return "color: -libweb-palette-syntax-keyword;";
case StyleType::ControlKeyword:
return "color: -libweb-palette-syntax-control-keyword;";
case StyleType::Identifier:
return "color: -libweb-palette-syntax-identifier;";
default:
ASSERT_NOT_REACHED();
}
}
MarkupGenerator::StyleType MarkupGenerator::style_type_for_token(Token token)
{
switch (token.category()) {
case TokenCategory::Invalid:
return StyleType::Invalid;
case TokenCategory::Number:
return StyleType::Number;
case TokenCategory::String:
return StyleType::String;
case TokenCategory::Punctuation:
return StyleType::Punctuation;
case TokenCategory::Operator:
return StyleType::Operator;
case TokenCategory::Keyword:
switch (token.type()) {
case TokenType::BoolLiteral:
case TokenType::NullLiteral:
return StyleType::KeywordBold;
default:
return StyleType::Keyword;
}
case TokenCategory::ControlKeyword:
return StyleType::ControlKeyword;
case TokenCategory::Identifier:
return StyleType::Identifier;
default:
dbgln("Unknown style type for token {}", token.name());
ASSERT_NOT_REACHED();
}
}
String MarkupGenerator::open_style_type(StyleType type)
{
return String::formatted("<span style=\"{}\">", style_from_style_type(type));
}
String MarkupGenerator::wrap_string_in_style(String source, StyleType type)
{
return String::formatted("<span style=\"{}\">{}</span>", style_from_style_type(type), escape_html_entities(source));
}
}

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) 2020, Hunter Salyer <thefalsehonesty@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/HashTable.h>
#include <AK/String.h>
#include <LibJS/Forward.h>
namespace JS {
class MarkupGenerator {
public:
static String html_from_source(const StringView&);
static String html_from_value(Value);
private:
enum class StyleType {
Invalid,
String,
Number,
KeywordBold,
Punctuation,
Operator,
Keyword,
ControlKeyword,
Identifier
};
static void value_to_html(Value, StringBuilder& output_html, HashTable<Object*> seen_objects = {});
static void array_to_html(const Array&, StringBuilder& output_html, HashTable<Object*>&);
static void object_to_html(const Object&, StringBuilder& output_html, HashTable<Object*>&);
static void function_to_html(const Object&, StringBuilder& output_html, HashTable<Object*>&);
static void date_to_html(const Object&, StringBuilder& output_html, HashTable<Object*>&);
static void error_to_html(const Object&, StringBuilder& output_html, HashTable<Object*>&);
static String style_from_style_type(StyleType);
static StyleType style_type_for_token(Token);
static String open_style_type(StyleType type);
static String wrap_string_in_style(String source, StyleType type);
};
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,227 @@
/*
* Copyright (c) 2020, Stephan Unverwerth <s.unverwerth@gmx.de>
* 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/HashTable.h>
#include <AK/NonnullRefPtr.h>
#include <AK/StringBuilder.h>
#include <LibJS/AST.h>
#include <LibJS/Lexer.h>
#include <LibJS/SourceRange.h>
#include <stdio.h>
namespace JS {
enum class Associativity {
Left,
Right
};
struct FunctionNodeParseOptions {
enum {
CheckForFunctionAndName = 1 << 0,
AllowSuperPropertyLookup = 1 << 1,
AllowSuperConstructorCall = 1 << 2,
IsGetterFunction = 1 << 3,
IsSetterFunction = 1 << 4,
IsArrowFunction = 1 << 5,
};
};
class Parser {
public:
explicit Parser(Lexer lexer);
NonnullRefPtr<Program> parse_program();
template<typename FunctionNodeType>
NonnullRefPtr<FunctionNodeType> parse_function_node(u8 parse_options = FunctionNodeParseOptions::CheckForFunctionAndName);
Vector<FunctionNode::Parameter> parse_function_parameters(int& function_length, u8 parse_options = 0);
NonnullRefPtr<Declaration> parse_declaration();
NonnullRefPtr<Statement> parse_statement();
NonnullRefPtr<BlockStatement> parse_block_statement();
NonnullRefPtr<BlockStatement> parse_block_statement(bool& is_strict);
NonnullRefPtr<ReturnStatement> parse_return_statement();
NonnullRefPtr<VariableDeclaration> parse_variable_declaration(bool for_loop_variable_declaration = false);
NonnullRefPtr<Statement> parse_for_statement();
NonnullRefPtr<Statement> parse_for_in_of_statement(NonnullRefPtr<ASTNode> lhs);
NonnullRefPtr<IfStatement> parse_if_statement();
NonnullRefPtr<ThrowStatement> parse_throw_statement();
NonnullRefPtr<TryStatement> parse_try_statement();
NonnullRefPtr<CatchClause> parse_catch_clause();
NonnullRefPtr<SwitchStatement> parse_switch_statement();
NonnullRefPtr<SwitchCase> parse_switch_case();
NonnullRefPtr<BreakStatement> parse_break_statement();
NonnullRefPtr<ContinueStatement> parse_continue_statement();
NonnullRefPtr<DoWhileStatement> parse_do_while_statement();
NonnullRefPtr<WhileStatement> parse_while_statement();
NonnullRefPtr<WithStatement> parse_with_statement();
NonnullRefPtr<DebuggerStatement> parse_debugger_statement();
NonnullRefPtr<ConditionalExpression> parse_conditional_expression(NonnullRefPtr<Expression> test);
NonnullRefPtr<Expression> parse_expression(int min_precedence, Associativity associate = Associativity::Right, Vector<TokenType> forbidden = {});
NonnullRefPtr<Expression> parse_primary_expression();
NonnullRefPtr<Expression> parse_unary_prefixed_expression();
NonnullRefPtr<RegExpLiteral> parse_regexp_literal();
NonnullRefPtr<ObjectExpression> parse_object_expression();
NonnullRefPtr<ArrayExpression> parse_array_expression();
NonnullRefPtr<StringLiteral> parse_string_literal(Token token, bool in_template_literal = false);
NonnullRefPtr<TemplateLiteral> parse_template_literal(bool is_tagged);
NonnullRefPtr<Expression> parse_secondary_expression(NonnullRefPtr<Expression>, int min_precedence, Associativity associate = Associativity::Right);
NonnullRefPtr<CallExpression> parse_call_expression(NonnullRefPtr<Expression>);
NonnullRefPtr<NewExpression> parse_new_expression();
NonnullRefPtr<ClassDeclaration> parse_class_declaration();
NonnullRefPtr<ClassExpression> parse_class_expression(bool expect_class_name);
NonnullRefPtr<Expression> parse_property_key();
NonnullRefPtr<AssignmentExpression> parse_assignment_expression(AssignmentOp, NonnullRefPtr<Expression> lhs, int min_precedence, Associativity);
RefPtr<FunctionExpression> try_parse_arrow_function_expression(bool expect_parens);
RefPtr<Statement> try_parse_labelled_statement();
RefPtr<MetaProperty> try_parse_new_target_expression();
struct Error {
String message;
Optional<Position> position;
String to_string() const
{
if (!position.has_value())
return message;
return String::formatted("{} (line: {}, column: {})", message, position.value().line, position.value().column);
}
String source_location_hint(const StringView& source, const char spacer = ' ', const char indicator = '^') const
{
if (!position.has_value())
return {};
// We need to modify the source to match what the lexer considers one line - normalizing
// line terminators to \n is easier than splitting using all different LT characters.
String source_string { source };
source_string.replace("\r\n", "\n");
source_string.replace("\r", "\n");
source_string.replace(LINE_SEPARATOR, "\n");
source_string.replace(PARAGRAPH_SEPARATOR, "\n");
StringBuilder builder;
builder.append(source_string.split_view('\n', true)[position.value().line - 1]);
builder.append('\n');
for (size_t i = 0; i < position.value().column - 1; ++i)
builder.append(spacer);
builder.append(indicator);
return builder.build();
}
};
bool has_errors() const { return m_parser_state.m_errors.size(); }
const Vector<Error>& errors() const { return m_parser_state.m_errors; }
void print_errors() const
{
for (auto& error : m_parser_state.m_errors) {
auto hint = error.source_location_hint(m_parser_state.m_lexer.source());
if (!hint.is_empty())
warnln("{}", hint);
warnln("SyntaxError: {}", error.to_string());
}
}
private:
friend class ScopePusher;
Associativity operator_associativity(TokenType) const;
bool match_expression() const;
bool match_unary_prefixed_expression() const;
bool match_secondary_expression(Vector<TokenType> forbidden = {}) const;
bool match_statement() const;
bool match_declaration() const;
bool match_variable_declaration() const;
bool match_identifier_name() const;
bool match_property_key() const;
bool match(TokenType type) const;
bool done() const;
void expected(const char* what);
void syntax_error(const String& message, Optional<Position> = {});
Token consume();
Token consume(TokenType type);
Token consume_and_validate_numeric_literal();
void consume_or_insert_semicolon();
void save_state();
void load_state();
void discard_saved_state();
Position position() const;
struct RulePosition {
AK_MAKE_NONCOPYABLE(RulePosition);
AK_MAKE_NONMOVABLE(RulePosition);
public:
RulePosition(Parser& parser, Position position)
: m_parser(parser)
, m_position(position)
{
m_parser.m_rule_starts.append(position);
}
~RulePosition()
{
auto last = m_parser.m_rule_starts.take_last();
ASSERT(last.line == m_position.line);
ASSERT(last.column == m_position.column);
}
const Position& position() const { return m_position; }
private:
Parser& m_parser;
Position m_position;
};
[[nodiscard]] RulePosition push_start() { return { *this, position() }; }
struct ParserState {
Lexer m_lexer;
Token m_current_token;
Vector<Error> m_errors;
Vector<NonnullRefPtrVector<VariableDeclaration>> m_var_scopes;
Vector<NonnullRefPtrVector<VariableDeclaration>> m_let_scopes;
Vector<NonnullRefPtrVector<FunctionDeclaration>> m_function_scopes;
HashTable<StringView> m_labels_in_scope;
bool m_strict_mode { false };
bool m_allow_super_property_lookup { false };
bool m_allow_super_constructor_call { false };
bool m_in_function_context { false };
bool m_in_arrow_function_context { false };
bool m_in_break_context { false };
bool m_in_continue_context { false };
bool m_string_legacy_octal_escape_sequence_in_scope { false };
explicit ParserState(Lexer);
};
Vector<Position> m_rule_starts;
ParserState m_parser_state;
Vector<ParserState> m_saved_state;
};
}

View file

@ -0,0 +1,82 @@
/*
* Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* 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/Function.h>
#include <LibJS/Runtime/VM.h>
namespace JS {
class Accessor final : public Cell {
public:
static Accessor* create(VM& vm, Function* getter, Function* setter)
{
return vm.heap().allocate_without_global_object<Accessor>(getter, setter);
}
Accessor(Function* getter, Function* setter)
: m_getter(getter)
, m_setter(setter)
{
}
Function* getter() const { return m_getter; }
void set_getter(Function* getter) { m_getter = getter; }
Function* setter() const { return m_setter; }
void set_setter(Function* setter) { m_setter = setter; }
Value call_getter(Value this_value)
{
if (!m_getter)
return js_undefined();
return vm().call(*m_getter, this_value);
}
void call_setter(Value this_value, Value setter_value)
{
if (!m_setter)
return;
// FIXME: It might be nice if we had a way to communicate to our caller if an exception happened after this.
[[maybe_unused]] auto rc = vm().call(*m_setter, this_value, setter_value);
}
void visit_edges(Cell::Visitor& visitor) override
{
visitor.visit(m_getter);
visitor.visit(m_setter);
}
private:
const char* class_name() const override { return "Accessor"; };
Function* m_getter { nullptr };
Function* m_setter { nullptr };
};
}

View file

@ -0,0 +1,86 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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 <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/ArrayPrototype.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/GlobalObject.h>
namespace JS {
Array* Array::create(GlobalObject& global_object)
{
return global_object.heap().allocate<Array>(global_object, *global_object.array_prototype());
}
Array::Array(Object& prototype)
: Object(prototype)
{
auto& vm = this->vm();
define_native_property(vm.names.length, length_getter, length_setter, Attribute::Writable);
}
Array::~Array()
{
}
Array* Array::typed_this(VM& vm, GlobalObject& global_object)
{
auto* this_object = vm.this_value(global_object).to_object(global_object);
if (!this_object)
return {};
if (!this_object->is_array()) {
vm.throw_exception<TypeError>(global_object, ErrorType::NotAn, "Array");
return nullptr;
}
return static_cast<Array*>(this_object);
}
JS_DEFINE_NATIVE_GETTER(Array::length_getter)
{
auto* array = typed_this(vm, global_object);
if (!array)
return {};
return Value(static_cast<i32>(array->indexed_properties().array_like_size()));
}
JS_DEFINE_NATIVE_SETTER(Array::length_setter)
{
auto* array = typed_this(vm, global_object);
if (!array)
return;
auto length = value.to_number(global_object);
if (vm.exception())
return;
if (length.is_nan() || length.is_infinity() || length.as_double() < 0) {
vm.throw_exception<RangeError>(global_object, ErrorType::InvalidLength, "array");
return;
}
array->indexed_properties().set_array_like_size(length.as_double());
}
}

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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 Array final : public Object {
JS_OBJECT(Array, Object);
public:
static Array* create(GlobalObject&);
explicit Array(Object& prototype);
virtual ~Array() override;
static Array* typed_this(VM&, GlobalObject&);
private:
virtual bool is_array() const override { return true; }
JS_DECLARE_NATIVE_GETTER(length_getter);
JS_DECLARE_NATIVE_SETTER(length_setter);
};
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* 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/Runtime/ArrayBuffer.h>
#include <LibJS/Runtime/GlobalObject.h>
namespace JS {
ArrayBuffer* ArrayBuffer::create(GlobalObject& global_object, size_t byte_size)
{
return global_object.heap().allocate<ArrayBuffer>(global_object, byte_size, *global_object.array_buffer_prototype());
}
ArrayBuffer::ArrayBuffer(size_t byte_size, Object& prototype)
: Object(prototype)
, m_buffer(ByteBuffer::create_zeroed(byte_size))
{
}
ArrayBuffer::~ArrayBuffer()
{
}
}

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* 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/ByteBuffer.h>
#include <LibJS/Runtime/Object.h>
namespace JS {
class ArrayBuffer : public Object {
JS_OBJECT(ArrayBuffer, Object);
public:
static ArrayBuffer* create(GlobalObject&, size_t);
ArrayBuffer(size_t, Object& prototype);
virtual ~ArrayBuffer() override;
size_t byte_length() const { return m_buffer.size(); }
ByteBuffer& buffer() { return m_buffer; }
const ByteBuffer& buffer() const { return m_buffer; }
private:
ByteBuffer m_buffer;
};
}

View file

@ -0,0 +1,85 @@
/*
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* 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/Runtime/ArrayBuffer.h>
#include <LibJS/Runtime/ArrayBufferConstructor.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/TypedArray.h>
namespace JS {
ArrayBufferConstructor::ArrayBufferConstructor(GlobalObject& global_object)
: NativeFunction(vm().names.ArrayBuffer, *global_object.function_prototype())
{
}
void ArrayBufferConstructor::initialize(GlobalObject& global_object)
{
auto& vm = this->vm();
NativeFunction::initialize(global_object);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_property(vm.names.prototype, global_object.array_buffer_prototype(), 0);
define_property(vm.names.length, Value(1), Attribute::Configurable);
define_native_function(vm.names.isView, is_view, 1, attr);
}
ArrayBufferConstructor::~ArrayBufferConstructor()
{
}
Value ArrayBufferConstructor::call()
{
auto& vm = this->vm();
vm.throw_exception<TypeError>(global_object(), ErrorType::ConstructorWithoutNew, vm.names.ArrayBuffer);
return {};
}
Value ArrayBufferConstructor::construct(Function&)
{
auto& vm = this->vm();
auto byte_length = vm.argument(0).to_index(global_object());
if (vm.exception()) {
// Re-throw more specific RangeError
vm.clear_exception();
vm.throw_exception<RangeError>(global_object(), ErrorType::InvalidLength, "array buffer");
return {};
}
return ArrayBuffer::create(global_object(), byte_length);
}
JS_DEFINE_NATIVE_FUNCTION(ArrayBufferConstructor::is_view)
{
auto arg = vm.argument(0);
if (!arg.is_object())
return Value(false);
if (arg.as_object().is_typed_array())
return Value(true);
// FIXME: Check for DataView as well
return Value(false);
}
}

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* 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 ArrayBufferConstructor final : public NativeFunction {
JS_OBJECT(ArrayBufferConstructor, NativeFunction);
public:
explicit ArrayBufferConstructor(GlobalObject&);
virtual void initialize(GlobalObject&) override;
virtual ~ArrayBufferConstructor() override;
virtual Value call() override;
virtual Value construct(Function& new_target) override;
private:
virtual bool has_constructor() const override { return true; }
JS_DECLARE_NATIVE_FUNCTION(is_view);
};
}

View file

@ -0,0 +1,87 @@
/*
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* 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 <LibJS/Runtime/ArrayBuffer.h>
#include <LibJS/Runtime/ArrayBufferPrototype.h>
#include <LibJS/Runtime/GlobalObject.h>
namespace JS {
ArrayBufferPrototype::ArrayBufferPrototype(GlobalObject& global_object)
: Object(*global_object.object_prototype())
{
}
void ArrayBufferPrototype::initialize(GlobalObject& global_object)
{
auto& vm = this->vm();
Object::initialize(global_object);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(vm.names.slice, slice, 2, attr);
// FIXME: This should be an accessor property
define_native_property(vm.names.byteLength, byte_length_getter, {}, Attribute::Configurable);
define_property(vm.well_known_symbol_to_string_tag(), js_string(vm.heap(), "ArrayBuffer"), Attribute::Configurable);
}
ArrayBufferPrototype::~ArrayBufferPrototype()
{
}
static ArrayBuffer* array_buffer_object_from(VM& vm, GlobalObject& global_object)
{
// ArrayBuffer.prototype.* deliberately don't coerce |this| value to object.
auto this_value = vm.this_value(global_object);
if (!this_value.is_object())
return nullptr;
auto& this_object = this_value.as_object();
if (!is<ArrayBuffer>(this_object)) {
vm.throw_exception<TypeError>(global_object, ErrorType::NotAn, "ArrayBuffer");
return nullptr;
}
return static_cast<ArrayBuffer*>(&this_object);
}
JS_DEFINE_NATIVE_FUNCTION(ArrayBufferPrototype::slice)
{
auto array_buffer_object = array_buffer_object_from(vm, global_object);
if (!array_buffer_object)
return {};
TODO();
}
JS_DEFINE_NATIVE_GETTER(ArrayBufferPrototype::byte_length_getter)
{
auto array_buffer_object = array_buffer_object_from(vm, global_object);
if (!array_buffer_object)
return {};
// FIXME: Check for shared buffer
// FIXME: Check for detached buffer
return Value((double)array_buffer_object->byte_length());
}
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* 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 ArrayBufferPrototype final : public Object {
JS_OBJECT(ArrayBufferPrototype, Object);
public:
explicit ArrayBufferPrototype(GlobalObject&);
virtual void initialize(GlobalObject&) override;
virtual ~ArrayBufferPrototype() override;
private:
JS_DECLARE_NATIVE_FUNCTION(slice);
JS_DECLARE_NATIVE_GETTER(byte_length_getter);
};
}

View file

@ -0,0 +1,141 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* 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 <LibJS/Heap/Heap.h>
#include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/ArrayConstructor.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/IteratorOperations.h>
#include <LibJS/Runtime/Shape.h>
namespace JS {
ArrayConstructor::ArrayConstructor(GlobalObject& global_object)
: NativeFunction(vm().names.Array, *global_object.function_prototype())
{
}
ArrayConstructor::~ArrayConstructor()
{
}
void ArrayConstructor::initialize(GlobalObject& global_object)
{
auto& vm = this->vm();
NativeFunction::initialize(global_object);
define_property(vm.names.prototype, global_object.array_prototype(), 0);
define_property(vm.names.length, Value(1), Attribute::Configurable);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(vm.names.from, from, 1, attr);
define_native_function(vm.names.isArray, is_array, 1, attr);
define_native_function(vm.names.of, of, 0, attr);
}
Value ArrayConstructor::call()
{
if (vm().argument_count() <= 0)
return Array::create(global_object());
if (vm().argument_count() == 1 && vm().argument(0).is_number()) {
auto array_length_value = vm().argument(0);
if (!array_length_value.is_integer() || array_length_value.as_i32() < 0) {
vm().throw_exception<RangeError>(global_object(), ErrorType::InvalidLength, "array");
return {};
}
auto* array = Array::create(global_object());
array->indexed_properties().set_array_like_size(array_length_value.as_i32());
return array;
}
auto* array = Array::create(global_object());
for (size_t i = 0; i < vm().argument_count(); ++i)
array->indexed_properties().append(vm().argument(i));
return array;
}
Value ArrayConstructor::construct(Function&)
{
return call();
}
JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from)
{
auto value = vm.argument(0);
auto object = value.to_object(global_object);
if (!object)
return {};
auto* array = Array::create(global_object);
// Array.from() lets you create Arrays from:
if (auto size = object->indexed_properties().array_like_size()) {
// * array-like objects (objects with a length property and indexed elements)
MarkedValueList elements(vm.heap());
elements.ensure_capacity(size);
for (size_t i = 0; i < size; ++i) {
elements.append(object->get(i));
if (vm.exception())
return {};
}
array->set_indexed_property_elements(move(elements));
} else {
// * iterable objects
get_iterator_values(global_object, value, [&](Value element) {
if (vm.exception())
return IterationDecision::Break;
array->indexed_properties().append(element);
return IterationDecision::Continue;
});
if (vm.exception())
return {};
}
// FIXME: if interpreter.argument_count() >= 2: mapFn
// FIXME: if interpreter.argument_count() >= 3: thisArg
return array;
}
JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::is_array)
{
auto value = vm.argument(0);
return Value(value.is_array());
}
JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::of)
{
auto* array = Array::create(global_object);
for (size_t i = 0; i < vm.argument_count(); ++i)
array->indexed_properties().append(vm.argument(i));
return array;
}
}

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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 ArrayConstructor final : public NativeFunction {
JS_OBJECT(ArrayConstructor, NativeFunction);
public:
explicit ArrayConstructor(GlobalObject&);
virtual void initialize(GlobalObject&) override;
virtual ~ArrayConstructor() override;
virtual Value call() override;
virtual Value construct(Function& new_target) override;
private:
virtual bool has_constructor() const override { return true; }
JS_DECLARE_NATIVE_FUNCTION(from);
JS_DECLARE_NATIVE_FUNCTION(is_array);
JS_DECLARE_NATIVE_FUNCTION(of);
};
}

View file

@ -0,0 +1,54 @@
/*
* 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/Runtime/ArrayIterator.h>
#include <LibJS/Runtime/GlobalObject.h>
namespace JS {
ArrayIterator* ArrayIterator::create(GlobalObject& global_object, Value array, Object::PropertyKind iteration_kind)
{
return global_object.heap().allocate<ArrayIterator>(global_object, *global_object.array_iterator_prototype(), array, iteration_kind);
}
ArrayIterator::ArrayIterator(Object& prototype, Value array, Object::PropertyKind iteration_kind)
: Object(prototype)
, m_array(array)
, m_iteration_kind(iteration_kind)
{
}
ArrayIterator::~ArrayIterator()
{
}
void ArrayIterator::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_array);
}
}

View file

@ -0,0 +1,56 @@
/*
* 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 ArrayIterator final : public Object {
JS_OBJECT(ArrayIterator, Object);
public:
static ArrayIterator* create(GlobalObject&, Value array, Object::PropertyKind iteration_kind);
explicit ArrayIterator(Object& prototype, Value array, Object::PropertyKind iteration_kind);
virtual ~ArrayIterator() override;
Value array() const { return m_array; }
Object::PropertyKind iteration_kind() const { return m_iteration_kind; }
size_t index() const { return m_index; }
private:
friend class ArrayIteratorPrototype;
virtual void visit_edges(Cell::Visitor&) override;
Value m_array;
Object::PropertyKind m_iteration_kind;
size_t m_index { 0 };
};
}

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.
*/
#include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/ArrayIterator.h>
#include <LibJS/Runtime/ArrayIteratorPrototype.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/IteratorOperations.h>
namespace JS {
ArrayIteratorPrototype::ArrayIteratorPrototype(GlobalObject& global_object)
: Object(*global_object.iterator_prototype())
{
}
void ArrayIteratorPrototype::initialize(GlobalObject& global_object)
{
auto& vm = this->vm();
Object::initialize(global_object);
define_native_function(vm.names.next, next, 0, Attribute::Configurable | Attribute::Writable);
define_property(global_object.vm().well_known_symbol_to_string_tag(), js_string(global_object.heap(), "Array Iterator"), Attribute::Configurable);
}
ArrayIteratorPrototype::~ArrayIteratorPrototype()
{
}
JS_DEFINE_NATIVE_FUNCTION(ArrayIteratorPrototype::next)
{
auto this_value = vm.this_value(global_object);
if (!this_value.is_object() || !is<ArrayIterator>(this_value.as_object())) {
vm.throw_exception<TypeError>(global_object, ErrorType::NotAn, "Array Iterator");
return {};
}
auto& this_object = this_value.as_object();
auto& iterator = static_cast<ArrayIterator&>(this_object);
auto target_array = iterator.array();
if (target_array.is_undefined())
return create_iterator_result_object(global_object, js_undefined(), true);
ASSERT(target_array.is_object());
auto& array = target_array.as_object();
auto index = iterator.index();
auto iteration_kind = iterator.iteration_kind();
// FIXME: Typed array check
auto length = array.indexed_properties().array_like_size();
if (index >= length) {
iterator.m_array = js_undefined();
return create_iterator_result_object(global_object, js_undefined(), true);
}
iterator.m_index++;
if (iteration_kind == Object::PropertyKind::Key)
return create_iterator_result_object(global_object, Value(static_cast<i32>(index)), false);
auto value = array.get(index);
if (vm.exception())
return {};
if (iteration_kind == Object::PropertyKind::Value)
return create_iterator_result_object(global_object, value, false);
auto* entry_array = Array::create(global_object);
entry_array->define_property(0, Value(static_cast<i32>(index)));
entry_array->define_property(1, value);
return create_iterator_result_object(global_object, entry_array, false);
}
}

View file

@ -0,0 +1,45 @@
/*
* 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 ArrayIteratorPrototype final : public Object {
JS_OBJECT(ArrayIteratorPrototype, Object)
public:
ArrayIteratorPrototype(GlobalObject&);
virtual void initialize(GlobalObject&) override;
virtual ~ArrayIteratorPrototype() override;
private:
JS_DECLARE_NATIVE_FUNCTION(next);
};
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,71 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* 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 ArrayPrototype final : public Object {
JS_OBJECT(ArrayPrototype, Object);
public:
ArrayPrototype(GlobalObject&);
virtual void initialize(GlobalObject&) override;
virtual ~ArrayPrototype() override;
private:
JS_DECLARE_NATIVE_FUNCTION(filter);
JS_DECLARE_NATIVE_FUNCTION(for_each);
JS_DECLARE_NATIVE_FUNCTION(map);
JS_DECLARE_NATIVE_FUNCTION(pop);
JS_DECLARE_NATIVE_FUNCTION(push);
JS_DECLARE_NATIVE_FUNCTION(shift);
JS_DECLARE_NATIVE_FUNCTION(to_string);
JS_DECLARE_NATIVE_FUNCTION(to_locale_string);
JS_DECLARE_NATIVE_FUNCTION(unshift);
JS_DECLARE_NATIVE_FUNCTION(join);
JS_DECLARE_NATIVE_FUNCTION(concat);
JS_DECLARE_NATIVE_FUNCTION(slice);
JS_DECLARE_NATIVE_FUNCTION(index_of);
JS_DECLARE_NATIVE_FUNCTION(reduce);
JS_DECLARE_NATIVE_FUNCTION(reduce_right);
JS_DECLARE_NATIVE_FUNCTION(reverse);
JS_DECLARE_NATIVE_FUNCTION(sort);
JS_DECLARE_NATIVE_FUNCTION(last_index_of);
JS_DECLARE_NATIVE_FUNCTION(includes);
JS_DECLARE_NATIVE_FUNCTION(find);
JS_DECLARE_NATIVE_FUNCTION(find_index);
JS_DECLARE_NATIVE_FUNCTION(some);
JS_DECLARE_NATIVE_FUNCTION(every);
JS_DECLARE_NATIVE_FUNCTION(splice);
JS_DECLARE_NATIVE_FUNCTION(fill);
JS_DECLARE_NATIVE_FUNCTION(values);
};
}

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* 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 <LibCrypto/BigInt/SignedBigInteger.h>
#include <LibJS/Heap/Heap.h>
#include <LibJS/Runtime/BigInt.h>
namespace JS {
BigInt::BigInt(Crypto::SignedBigInteger big_integer)
: m_big_integer(move(big_integer))
{
ASSERT(!m_big_integer.is_invalid());
}
BigInt::~BigInt()
{
}
BigInt* js_bigint(Heap& heap, Crypto::SignedBigInteger big_integer)
{
return heap.allocate_without_global_object<BigInt>(move(big_integer));
}
}

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* 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 <LibCrypto/BigInt/SignedBigInteger.h>
#include <LibJS/Runtime/Cell.h>
namespace JS {
class BigInt final : public Cell {
public:
BigInt(Crypto::SignedBigInteger);
virtual ~BigInt();
const Crypto::SignedBigInteger& big_integer() const { return m_big_integer; }
const String to_string() const { return String::formatted("{}n", m_big_integer.to_base10()); }
private:
virtual const char* class_name() const override { return "BigInt"; }
Crypto::SignedBigInteger m_big_integer;
};
BigInt* js_bigint(Heap&, Crypto::SignedBigInteger);
}

View file

@ -0,0 +1,92 @@
/*
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* 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/String.h>
#include <LibCrypto/BigInt/SignedBigInteger.h>
#include <LibJS/Runtime/BigIntConstructor.h>
#include <LibJS/Runtime/BigIntObject.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/VM.h>
namespace JS {
BigIntConstructor::BigIntConstructor(GlobalObject& global_object)
: NativeFunction(vm().names.BigInt, *global_object.function_prototype())
{
}
void BigIntConstructor::initialize(GlobalObject& global_object)
{
auto& vm = this->vm();
NativeFunction::initialize(global_object);
define_property(vm.names.prototype, global_object.bigint_prototype(), 0);
define_property(vm.names.length, Value(1), Attribute::Configurable);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(vm.names.asIntN, as_int_n, 2, attr);
define_native_function(vm.names.asUintN, as_uint_n, 2, attr);
}
BigIntConstructor::~BigIntConstructor()
{
}
Value BigIntConstructor::call()
{
auto primitive = vm().argument(0).to_primitive(Value::PreferredType::Number);
if (vm().exception())
return {};
if (primitive.is_number()) {
if (!primitive.is_integer()) {
vm().throw_exception<RangeError>(global_object(), ErrorType::BigIntIntArgument);
return {};
}
return js_bigint(heap(), Crypto::SignedBigInteger { primitive.as_i32() });
}
auto* bigint = vm().argument(0).to_bigint(global_object());
if (vm().exception())
return {};
return bigint;
}
Value BigIntConstructor::construct(Function&)
{
vm().throw_exception<TypeError>(global_object(), ErrorType::NotAConstructor, "BigInt");
return {};
}
JS_DEFINE_NATIVE_FUNCTION(BigIntConstructor::as_int_n)
{
TODO();
}
JS_DEFINE_NATIVE_FUNCTION(BigIntConstructor::as_uint_n)
{
TODO();
}
}

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* 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 BigIntConstructor final : public NativeFunction {
JS_OBJECT(BigIntConstructor, NativeFunction);
public:
explicit BigIntConstructor(GlobalObject&);
virtual void initialize(GlobalObject&) override;
virtual ~BigIntConstructor() override;
virtual Value call() override;
virtual Value construct(Function& new_target) override;
private:
virtual bool has_constructor() const override { return true; }
JS_DECLARE_NATIVE_FUNCTION(as_int_n);
JS_DECLARE_NATIVE_FUNCTION(as_uint_n);
};
}

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* 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/Runtime/BigIntObject.h>
#include <LibJS/Runtime/GlobalObject.h>
namespace JS {
BigIntObject* BigIntObject::create(GlobalObject& global_object, BigInt& bigint)
{
return global_object.heap().allocate<BigIntObject>(global_object, bigint, *global_object.bigint_prototype());
}
BigIntObject::BigIntObject(BigInt& bigint, Object& prototype)
: Object(prototype)
, m_bigint(bigint)
{
}
BigIntObject::~BigIntObject()
{
}
void BigIntObject::visit_edges(Cell::Visitor& visitor)
{
Object::visit_edges(visitor);
visitor.visit(&m_bigint);
}
}

View file

@ -0,0 +1,55 @@
/*
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* 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/BigInt.h>
#include <LibJS/Runtime/Object.h>
namespace JS {
class BigIntObject final : public Object {
JS_OBJECT(BigIntObject, Object);
public:
static BigIntObject* create(GlobalObject&, BigInt&);
BigIntObject(BigInt&, Object& prototype);
virtual ~BigIntObject();
const BigInt& bigint() const { return m_bigint; }
virtual Value value_of() const override
{
return Value(&m_bigint);
}
private:
virtual void visit_edges(Visitor&) override;
BigInt& m_bigint;
};
}

View file

@ -0,0 +1,89 @@
/*
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* 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 <LibJS/Runtime/BigIntObject.h>
#include <LibJS/Runtime/BigIntPrototype.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/GlobalObject.h>
namespace JS {
BigIntPrototype::BigIntPrototype(GlobalObject& global_object)
: Object(*global_object.object_prototype())
{
}
void BigIntPrototype::initialize(GlobalObject& global_object)
{
auto& vm = this->vm();
Object::initialize(global_object);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(vm.names.toString, to_string, 0, attr);
define_native_function(vm.names.toLocaleString, to_locale_string, 0, attr);
define_native_function(vm.names.valueOf, value_of, 0, attr);
define_property(vm.well_known_symbol_to_string_tag(), js_string(global_object.heap(), "BigInt"), Attribute::Configurable);
}
BigIntPrototype::~BigIntPrototype()
{
}
static BigIntObject* bigint_object_from(VM& vm, GlobalObject& global_object)
{
auto* this_object = vm.this_value(global_object).to_object(global_object);
if (!this_object)
return nullptr;
if (!is<BigIntObject>(this_object)) {
vm.throw_exception<TypeError>(global_object, ErrorType::NotA, "BigInt");
return nullptr;
}
return static_cast<BigIntObject*>(this_object);
}
JS_DEFINE_NATIVE_FUNCTION(BigIntPrototype::to_string)
{
auto* bigint_object = bigint_object_from(vm, global_object);
if (!bigint_object)
return {};
return js_string(vm, bigint_object->bigint().big_integer().to_base10());
}
JS_DEFINE_NATIVE_FUNCTION(BigIntPrototype::to_locale_string)
{
return to_string(vm, global_object);
}
JS_DEFINE_NATIVE_FUNCTION(BigIntPrototype::value_of)
{
auto* bigint_object = bigint_object_from(vm, global_object);
if (!bigint_object)
return {};
return bigint_object->value_of();
}
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* 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 BigIntPrototype final : public Object {
JS_OBJECT(BigIntPrototype, Object);
public:
explicit BigIntPrototype(GlobalObject&);
virtual void initialize(GlobalObject&) override;
virtual ~BigIntPrototype() override;
private:
JS_DECLARE_NATIVE_FUNCTION(to_string);
JS_DECLARE_NATIVE_FUNCTION(to_locale_string);
JS_DECLARE_NATIVE_FUNCTION(value_of);
};
}

View file

@ -0,0 +1,62 @@
/*
* Copyright (c) 2020, Jack Karamanian <karamanian.jack@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/Runtime/BooleanConstructor.h>
#include <LibJS/Runtime/BooleanObject.h>
#include <LibJS/Runtime/BooleanPrototype.h>
#include <LibJS/Runtime/GlobalObject.h>
namespace JS {
BooleanConstructor::BooleanConstructor(GlobalObject& global_object)
: NativeFunction(vm().names.Boolean, *global_object.function_prototype())
{
}
void BooleanConstructor::initialize(GlobalObject& global_object)
{
auto& vm = this->vm();
NativeFunction::initialize(global_object);
define_property(vm.names.prototype, Value(global_object.boolean_prototype()), 0);
define_property(vm.names.length, Value(1), Attribute::Configurable);
}
BooleanConstructor::~BooleanConstructor()
{
}
Value BooleanConstructor::call()
{
return Value(vm().argument(0).to_boolean());
}
Value BooleanConstructor::construct(Function&)
{
return BooleanObject::create(global_object(), vm().argument(0).to_boolean());
}
}

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2020, Jack Karamanian <karamanian.jack@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 BooleanConstructor final : public NativeFunction {
JS_OBJECT(BooleanConstructor, NativeFunction);
public:
explicit BooleanConstructor(GlobalObject&);
virtual void initialize(GlobalObject&) override;
virtual ~BooleanConstructor() override;
virtual Value call() override;
virtual Value construct(Function& new_target) override;
private:
virtual bool has_constructor() const override { return true; }
};
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2020, Jack Karamanian <karamanian.jack@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/Runtime/BooleanObject.h>
#include <LibJS/Runtime/GlobalObject.h>
namespace JS {
BooleanObject* BooleanObject::create(GlobalObject& global_object, bool value)
{
return global_object.heap().allocate<BooleanObject>(global_object, value, *global_object.boolean_prototype());
}
BooleanObject::BooleanObject(bool value, Object& prototype)
: Object(prototype)
, m_value(value)
{
}
BooleanObject::~BooleanObject()
{
}
}

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2020, Jack Karamanian <karamanian.jack@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 BooleanObject : public Object {
JS_OBJECT(BooleanObject, Object);
public:
static BooleanObject* create(GlobalObject&, bool);
BooleanObject(bool, Object& prototype);
virtual ~BooleanObject() override;
virtual Value value_of() const override
{
return Value(m_value);
}
private:
bool m_value { false };
};
}

View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 2020, Jack Karamanian <karamanian.jack@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 <LibJS/Runtime/BooleanPrototype.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/GlobalObject.h>
namespace JS {
BooleanPrototype::BooleanPrototype(GlobalObject& global_object)
: BooleanObject(false, *global_object.object_prototype())
{
}
void BooleanPrototype::initialize(GlobalObject& global_object)
{
auto& vm = this->vm();
BooleanObject::initialize(global_object);
define_native_function(vm.names.toString, to_string, 0, Attribute::Writable | Attribute::Configurable);
define_native_function(vm.names.valueOf, value_of, 0, Attribute::Writable | Attribute::Configurable);
}
BooleanPrototype::~BooleanPrototype()
{
}
JS_DEFINE_NATIVE_FUNCTION(BooleanPrototype::to_string)
{
auto this_value = vm.this_value(global_object);
if (this_value.is_boolean())
return js_string(vm, this_value.as_bool() ? "true" : "false");
if (!this_value.is_object() || !is<BooleanObject>(this_value.as_object())) {
vm.throw_exception<TypeError>(global_object, ErrorType::NotA, "Boolean");
return {};
}
bool bool_value = static_cast<const BooleanObject&>(this_value.as_object()).value_of().as_bool();
return js_string(vm, bool_value ? "true" : "false");
}
JS_DEFINE_NATIVE_FUNCTION(BooleanPrototype::value_of)
{
auto this_value = vm.this_value(global_object);
if (this_value.is_boolean())
return this_value;
if (!this_value.is_object() || !is<BooleanObject>(this_value.as_object())) {
vm.throw_exception<TypeError>(global_object, ErrorType::NotA, "Boolean");
return {};
}
return static_cast<const BooleanObject&>(this_value.as_object()).value_of();
}
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2020, Jack Karamanian <karamanian.jack@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/BooleanObject.h>
namespace JS {
class BooleanPrototype final : public BooleanObject {
JS_OBJECT(BooleanPrototype, BooleanObject);
public:
explicit BooleanPrototype(GlobalObject&);
virtual void initialize(GlobalObject&) override;
virtual ~BooleanPrototype() override;
private:
JS_DECLARE_NATIVE_FUNCTION(to_string);
JS_DECLARE_NATIVE_FUNCTION(value_of);
};
}

View file

@ -0,0 +1,79 @@
/*
* Copyright (c) 2020, Jack Karamanian <karamanian.jack@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/Runtime/BoundFunction.h>
#include <LibJS/Runtime/GlobalObject.h>
namespace JS {
BoundFunction::BoundFunction(GlobalObject& global_object, Function& target_function, Value bound_this, Vector<Value> arguments, i32 length, Object* constructor_prototype)
: Function::Function(*global_object.function_prototype(), bound_this, move(arguments))
, m_target_function(&target_function)
, m_constructor_prototype(constructor_prototype)
, m_name(String::formatted("bound {}", target_function.name()))
, m_length(length)
{
}
void BoundFunction::initialize(GlobalObject& global_object)
{
auto& vm = this->vm();
Function::initialize(global_object);
define_property(vm.names.length, Value(m_length), Attribute::Configurable);
}
BoundFunction::~BoundFunction()
{
}
Value BoundFunction::call()
{
return m_target_function->call();
}
Value BoundFunction::construct(Function& new_target)
{
if (auto this_value = vm().this_value(global_object()); m_constructor_prototype && this_value.is_object()) {
this_value.as_object().set_prototype(m_constructor_prototype);
if (vm().exception())
return {};
}
return m_target_function->construct(new_target);
}
LexicalEnvironment* BoundFunction::create_environment()
{
return m_target_function->create_environment();
}
void BoundFunction::visit_edges(Visitor& visitor)
{
Function::visit_edges(visitor);
visitor.visit(m_target_function);
visitor.visit(m_constructor_prototype);
}
}

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 2020, Jack Karamanian <karamanian.jack@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/Function.h>
namespace JS {
class BoundFunction final : public Function {
JS_OBJECT(BoundFunction, Function);
public:
BoundFunction(GlobalObject&, Function& target_function, Value bound_this, Vector<Value> arguments, i32 length, Object* constructor_prototype);
virtual void initialize(GlobalObject&) override;
virtual ~BoundFunction();
virtual Value call() override;
virtual Value construct(Function& new_target) override;
virtual LexicalEnvironment* create_environment() override;
virtual void visit_edges(Visitor&) override;
virtual const FlyString& name() const override
{
return m_name;
}
Function& target_function() const
{
return *m_target_function;
}
virtual bool is_strict_mode() const override { return m_target_function->is_strict_mode(); }
private:
Function* m_target_function = nullptr;
Object* m_constructor_prototype = nullptr;
FlyString m_name;
i32 m_length { 0 };
};
}

View file

@ -0,0 +1,58 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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/Heap/HeapBlock.h>
#include <LibJS/Runtime/Cell.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/PrimitiveString.h>
#include <LibJS/Runtime/Value.h>
namespace JS {
void Cell::Visitor::visit(Cell* cell)
{
if (cell)
visit_impl(cell);
}
void Cell::Visitor::visit(Value value)
{
if (value.is_cell())
visit_impl(value.as_cell());
}
Heap& Cell::heap() const
{
return HeapBlock::from_cell(this)->heap();
}
VM& Cell::vm() const
{
return heap().vm();
}
}

View file

@ -0,0 +1,87 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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/Format.h>
#include <AK/Forward.h>
#include <AK/Noncopyable.h>
#include <AK/String.h>
#include <AK/TypeCasts.h>
#include <LibJS/Forward.h>
namespace JS {
class Cell {
AK_MAKE_NONCOPYABLE(Cell);
AK_MAKE_NONMOVABLE(Cell);
public:
virtual void initialize(GlobalObject&) { }
virtual ~Cell() { }
bool is_marked() const { return m_mark; }
void set_marked(bool b) { m_mark = b; }
bool is_live() const { return m_live; }
void set_live(bool b) { m_live = b; }
virtual const char* class_name() const = 0;
class Visitor {
public:
void visit(Cell*);
void visit(Value);
protected:
virtual void visit_impl(Cell*) = 0;
};
virtual void visit_edges(Visitor&) { }
Heap& heap() const;
VM& vm() const;
protected:
Cell() { }
private:
bool m_mark { false };
bool m_live { true };
};
}
template<>
struct AK::Formatter<JS::Cell> : AK::Formatter<FormatString> {
void format(FormatBuilder& builder, const JS::Cell* cell)
{
if (!cell)
Formatter<FormatString>::format(builder, "Cell{nullptr}");
else
Formatter<FormatString>::format(builder, "{}({})", cell->class_name(), cell);
}
};

View file

@ -0,0 +1,255 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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/FlyString.h>
#include <LibJS/Forward.h>
namespace JS {
#define ENUMERATE_STANDARD_PROPERTY_NAMES(P) \
P(BYTES_PER_ELEMENT) \
P(BigInt) \
P(Boolean) \
P(E) \
P(EPSILON) \
P(Infinity) \
P(JSON) \
P(LN10) \
P(LN2) \
P(LOG10E) \
P(LOG2E) \
P(MAX_SAFE_INTEGER) \
P(MIN_SAFE_INTEGER) \
P(Math) \
P(NEGATIVE_INFINITY) \
P(NaN) \
P(Number) \
P(PI) \
P(POSITIVE_INFINITY) \
P(Proxy) \
P(Reflect) \
P(RegExp) \
P(SQRT1_2) \
P(SQRT2) \
P(String) \
P(Symbol) \
P(UTC) \
P(abs) \
P(acos) \
P(acosh) \
P(apply) \
P(arguments) \
P(asIntN) \
P(asUintN) \
P(asin) \
P(asinh) \
P(atan) \
P(atan2) \
P(atanh) \
P(bind) \
P(byteLength) \
P(call) \
P(cbrt) \
P(ceil) \
P(charAt) \
P(charCodeAt) \
P(clear) \
P(clz32) \
P(concat) \
P(configurable) \
P(console) \
P(construct) \
P(constructor) \
P(cos) \
P(cosh) \
P(count) \
P(countReset) \
P(debug) \
P(defineProperty) \
P(deleteProperty) \
P(description) \
P(done) \
P(dotAll) \
P(endsWith) \
P(entries) \
P(enumerable) \
P(error) \
P(every) \
P(exec) \
P(exp) \
P(expm1) \
P(fill) \
P(filter) \
P(find) \
P(findIndex) \
P(flags) \
P(floor) \
P(forEach) \
P(from) \
P(fromCharCode) \
P(fround) \
P(gc) \
P(get) \
P(getDate) \
P(getDay) \
P(getFullYear) \
P(getHours) \
P(getMilliseconds) \
P(getMinutes) \
P(getMonth) \
P(getOwnPropertyDescriptor) \
P(getOwnPropertyNames) \
P(getPrototypeOf) \
P(getSeconds) \
P(getTime) \
P(getUTCDate) \
P(getUTCDay) \
P(getUTCFullYear) \
P(getUTCHours) \
P(getUTCMilliseconds) \
P(getUTCMinutes) \
P(getUTCMonth) \
P(getUTCSeconds) \
P(global) \
P(globalThis) \
P(groups) \
P(has) \
P(hasOwnProperty) \
P(hypot) \
P(ignoreCase) \
P(imul) \
P(includes) \
P(index) \
P(indexOf) \
P(info) \
P(input) \
P(is) \
P(isArray) \
P(isExtensible) \
P(isFinite) \
P(isInteger) \
P(isNaN) \
P(isPrototypeOf) \
P(isSafeInteger) \
P(isView) \
P(join) \
P(keyFor) \
P(keys) \
P(lastIndex) \
P(lastIndexOf) \
P(length) \
P(log) \
P(log1p) \
P(log2) \
P(log10) \
P(map) \
P(max) \
P(message) \
P(min) \
P(multiline) \
P(name) \
P(next) \
P(now) \
P(of) \
P(ownKeys) \
P(padEnd) \
P(padStart) \
P(parse) \
P(parseFloat) \
P(parseInt) \
P(pop) \
P(pow) \
P(preventExtensions) \
P(propertyIsEnumerable) \
P(prototype) \
P(push) \
P(random) \
P(raw) \
P(reduce) \
P(reduceRight) \
P(repeat) \
P(reverse) \
P(round) \
P(set) \
P(setPrototypeOf) \
P(shift) \
P(sign) \
P(sin) \
P(sinh) \
P(slice) \
P(some) \
P(sort) \
P(source) \
P(splice) \
P(sqrt) \
P(startsWith) \
P(sticky) \
P(stringify) \
P(substr) \
P(substring) \
P(tan) \
P(tanh) \
P(test) \
P(toDateString) \
P(toISOString) \
P(toJSON) \
P(toLocaleDateString) \
P(toLocaleString) \
P(toLocaleTimeString) \
P(toLowerCase) \
P(toString) \
P(toTimeString) \
P(toUpperCase) \
P(trace) \
P(trim) \
P(trimEnd) \
P(trimStart) \
P(trunc) \
P(undefined) \
P(unicode) \
P(unshift) \
P(value) \
P(valueOf) \
P(values) \
P(warn) \
P(writable)
struct CommonPropertyNames {
FlyString for_ { "for" };
#define __ENUMERATE(x) FlyString x { #x };
ENUMERATE_STANDARD_PROPERTY_NAMES(__ENUMERATE)
#undef __ENUMERATE
#define __JS_ENUMERATE(x, a, b, c, t) FlyString x { #x };
JS_ENUMERATE_BUILTIN_TYPES
#undef __JS_ENUMERATE
#define __JS_ENUMERATE(x, a) FlyString x { #x };
JS_ENUMERATE_WELL_KNOWN_SYMBOLS
#undef __JS_ENUMERATE
};
}

View file

@ -0,0 +1,106 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* Copyright (c) 2020, Emanuele Torre <torreemanuele6@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/FlyString.h>
#include <AK/Function.h>
#include <LibJS/Console.h>
#include <LibJS/Runtime/ConsoleObject.h>
#include <LibJS/Runtime/GlobalObject.h>
namespace JS {
ConsoleObject::ConsoleObject(GlobalObject& global_object)
: Object(*global_object.object_prototype())
{
}
void ConsoleObject::initialize(GlobalObject& global_object)
{
auto& vm = this->vm();
Object::initialize(global_object);
define_native_function(vm.names.log, log);
define_native_function(vm.names.debug, debug);
define_native_function(vm.names.info, info);
define_native_function(vm.names.warn, warn);
define_native_function(vm.names.error, error);
define_native_function(vm.names.trace, trace);
define_native_function(vm.names.count, count);
define_native_function(vm.names.countReset, count_reset);
define_native_function(vm.names.clear, clear);
}
ConsoleObject::~ConsoleObject()
{
}
JS_DEFINE_NATIVE_FUNCTION(ConsoleObject::log)
{
return global_object.console().log();
}
JS_DEFINE_NATIVE_FUNCTION(ConsoleObject::debug)
{
return global_object.console().debug();
}
JS_DEFINE_NATIVE_FUNCTION(ConsoleObject::info)
{
return global_object.console().info();
}
JS_DEFINE_NATIVE_FUNCTION(ConsoleObject::warn)
{
return global_object.console().warn();
}
JS_DEFINE_NATIVE_FUNCTION(ConsoleObject::error)
{
return global_object.console().error();
}
JS_DEFINE_NATIVE_FUNCTION(ConsoleObject::trace)
{
return global_object.console().trace();
}
JS_DEFINE_NATIVE_FUNCTION(ConsoleObject::count)
{
return global_object.console().count();
}
JS_DEFINE_NATIVE_FUNCTION(ConsoleObject::count_reset)
{
return global_object.console().count_reset();
}
JS_DEFINE_NATIVE_FUNCTION(ConsoleObject::clear)
{
return global_object.console().clear();
}
}

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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 ConsoleObject final : public Object {
JS_OBJECT(ConsoleObject, Object);
public:
explicit ConsoleObject(GlobalObject&);
virtual void initialize(GlobalObject&) override;
virtual ~ConsoleObject() override;
private:
JS_DECLARE_NATIVE_FUNCTION(log);
JS_DECLARE_NATIVE_FUNCTION(debug);
JS_DECLARE_NATIVE_FUNCTION(info);
JS_DECLARE_NATIVE_FUNCTION(warn);
JS_DECLARE_NATIVE_FUNCTION(error);
JS_DECLARE_NATIVE_FUNCTION(trace);
JS_DECLARE_NATIVE_FUNCTION(count);
JS_DECLARE_NATIVE_FUNCTION(count_reset);
JS_DECLARE_NATIVE_FUNCTION(clear);
};
}

View file

@ -0,0 +1,124 @@
/*
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* 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/StringBuilder.h>
#include <LibCore/DateTime.h>
#include <LibJS/Heap/Heap.h>
#include <LibJS/Runtime/Date.h>
#include <LibJS/Runtime/GlobalObject.h>
namespace JS {
Date* Date::create(GlobalObject& global_object, Core::DateTime datetime, u16 milliseconds)
{
return global_object.heap().allocate<Date>(global_object, datetime, milliseconds, *global_object.date_prototype());
}
Date::Date(Core::DateTime datetime, u16 milliseconds, Object& prototype)
: Object(prototype)
, m_datetime(datetime)
, m_milliseconds(milliseconds)
{
}
Date::~Date()
{
}
tm Date::to_utc_tm() const
{
time_t timestamp = m_datetime.timestamp();
struct tm tm;
gmtime_r(&timestamp, &tm);
return tm;
}
int Date::utc_date() const
{
return to_utc_tm().tm_mday;
}
int Date::utc_day() const
{
return to_utc_tm().tm_wday;
}
int Date::utc_full_year() const
{
return to_utc_tm().tm_year + 1900;
}
int Date::utc_hours() const
{
return to_utc_tm().tm_hour;
}
int Date::utc_minutes() const
{
return to_utc_tm().tm_min;
}
int Date::utc_month() const
{
return to_utc_tm().tm_mon;
}
int Date::utc_seconds() const
{
return to_utc_tm().tm_sec;
}
String Date::iso_date_string() const
{
auto tm = to_utc_tm();
int year = tm.tm_year + 1900;
int month = tm.tm_mon + 1;
StringBuilder builder;
if (year < 0)
builder.appendf("-%06d", -year);
else if (year > 9999)
builder.appendf("+%06d", year);
else
builder.appendf("%04d", year);
builder.append('-');
builder.appendf("%02d", month);
builder.append('-');
builder.appendf("%02d", tm.tm_mday);
builder.append('T');
builder.appendf("%02d", tm.tm_hour);
builder.append(':');
builder.appendf("%02d", tm.tm_min);
builder.append(':');
builder.appendf("%02d", tm.tm_sec);
builder.append('.');
builder.appendf("%03d", m_milliseconds);
builder.append('Z');
return builder.build();
}
}

View file

@ -0,0 +1,93 @@
/*
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* 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 <LibCore/DateTime.h>
#include <LibJS/Runtime/Object.h>
namespace JS {
class Date final : public Object {
JS_OBJECT(Date, Object);
public:
static Date* create(GlobalObject&, Core::DateTime, u16 milliseconds);
Date(Core::DateTime datetime, u16 milliseconds, Object& prototype);
virtual ~Date() override;
Core::DateTime& datetime() { return m_datetime; }
const Core::DateTime& datetime() const { return m_datetime; }
int date() const { return datetime().day(); }
int day() const { return datetime().weekday(); }
int full_year() const { return datetime().year(); }
int hours() const { return datetime().hour(); }
u16 milliseconds() const { return m_milliseconds; }
int minutes() const { return datetime().minute(); }
int month() const { return datetime().month() - 1; }
int seconds() const { return datetime().second(); }
double time() const { return datetime().timestamp() * 1000.0 + milliseconds(); }
int year() const { return datetime().day(); }
int utc_date() const;
int utc_day() const;
int utc_full_year() const;
int utc_hours() const;
int utc_milliseconds() const { return milliseconds(); }
int utc_minutes() const;
int utc_month() const;
int utc_seconds() const;
String date_string() const { return m_datetime.to_string("%a %b %d %Y"); }
// FIXME: Deal with timezones once SerenityOS has a working tzset(3)
String time_string() const { return m_datetime.to_string("%T GMT+0000 (UTC)"); }
String string() const
{
return String::formatted("{} {}", date_string(), time_string());
}
String iso_date_string() const;
// FIXME: One day, implement real locale support. Until then, everyone gets what the Clock MenuApplet displays.
String locale_date_string() const { return m_datetime.to_string("%Y-%m-%d"); }
String locale_string() const { return m_datetime.to_string(); }
String locale_time_string() const { return m_datetime.to_string("%H:%M:%S"); }
virtual Value value_of() const override
{
return Value(static_cast<double>(m_datetime.timestamp() * 1000 + m_milliseconds));
}
private:
tm to_utc_tm() const;
Core::DateTime m_datetime;
u16 m_milliseconds;
};
}

View file

@ -0,0 +1,252 @@
/*
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* Copyright (c) 2020, Nico Weber <thakis@chromium.org>
* 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/GenericLexer.h>
#include <LibCore/DateTime.h>
#include <LibJS/Runtime/Date.h>
#include <LibJS/Runtime/DateConstructor.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/VM.h>
#include <ctype.h>
#include <sys/time.h>
#include <time.h>
namespace JS {
static Value parse_simplified_iso8601(const String& iso_8601)
{
// Date.parse() is allowed to accept many formats. We strictly only accept things matching
// http://www.ecma-international.org/ecma-262/#sec-date-time-string-format
GenericLexer lexer(iso_8601);
auto lex_n_digits = [&](size_t n, int& out) {
if (lexer.tell_remaining() < n)
return false;
int r = 0;
for (size_t i = 0; i < n; ++i) {
char ch = lexer.consume();
if (!isdigit(ch))
return false;
r = 10 * r + ch - '0';
}
out = r;
return true;
};
int year = -1, month = -1, day = -1;
int hours = -1, minutes = -1, seconds = -1, milliseconds = -1;
char timezone = -1;
int timezone_hours = -1, timezone_minutes = -1;
auto lex_year = [&]() {
if (lexer.consume_specific('+'))
return lex_n_digits(6, year);
if (lexer.consume_specific('-')) {
int absolute_year;
if (!lex_n_digits(6, absolute_year))
return false;
year = -absolute_year;
return true;
}
return lex_n_digits(4, year);
};
auto lex_month = [&]() { return lex_n_digits(2, month) && month >= 1 && month <= 12; };
auto lex_day = [&]() { return lex_n_digits(2, day) && day >= 1 && day <= 31; };
auto lex_date = [&]() { return lex_year() && (!lexer.consume_specific('-') || (lex_month() && (!lexer.consume_specific('-') || lex_day()))); };
auto lex_hours_minutes = [&](int& out_h, int& out_m) {
int h, m;
if (lex_n_digits(2, h) && h >= 0 && h <= 24 && lexer.consume_specific(':') && lex_n_digits(2, m) && m >= 0 && m <= 59) {
out_h = h;
out_m = m;
return true;
}
return false;
};
auto lex_seconds = [&]() { return lex_n_digits(2, seconds) && seconds >= 0 && seconds <= 59; };
auto lex_milliseconds = [&]() { return lex_n_digits(3, milliseconds); };
auto lex_seconds_milliseconds = [&]() { return lex_seconds() && (!lexer.consume_specific('.') || lex_milliseconds()); };
auto lex_timezone = [&]() {
if (lexer.consume_specific('+')) {
timezone = '+';
return lex_hours_minutes(timezone_hours, timezone_minutes);
}
if (lexer.consume_specific('-')) {
timezone = '-';
return lex_hours_minutes(timezone_hours, timezone_minutes);
}
if (lexer.consume_specific('Z'))
timezone = 'Z';
return true;
};
auto lex_time = [&]() { return lex_hours_minutes(hours, minutes) && (!lexer.consume_specific(':') || lex_seconds_milliseconds()) && lex_timezone(); };
if (!lex_date() || (lexer.consume_specific('T') && !lex_time()) || !lexer.is_eof()) {
return js_nan();
}
// We parsed a valid date simplified ISO 8601 string. Values not present in the string are -1.
ASSERT(year != -1); // A valid date string always has at least a year.
struct tm tm = {};
tm.tm_year = year - 1900;
tm.tm_mon = month == -1 ? 0 : month - 1;
tm.tm_mday = day == -1 ? 1 : day;
tm.tm_hour = hours == -1 ? 0 : hours;
tm.tm_min = minutes == -1 ? 0 : minutes;
tm.tm_sec = seconds == -1 ? 0 : seconds;
// http://www.ecma-international.org/ecma-262/#sec-date.parse:
// "When the UTC offset representation is absent, date-only forms are interpreted as a UTC time and date-time forms are interpreted as a local time."
time_t timestamp;
if (timezone != -1 || hours == -1)
timestamp = timegm(&tm);
else
timestamp = mktime(&tm);
if (timezone == '-')
timestamp += (timezone_hours * 60 + timezone_minutes) * 60;
else if (timezone == '+')
timestamp -= (timezone_hours * 60 + timezone_minutes) * 60;
// FIXME: reject timestamp if resulting value wouldn't fit in a double
if (milliseconds == -1)
milliseconds = 0;
return Value(1000.0 * timestamp + milliseconds);
}
DateConstructor::DateConstructor(GlobalObject& global_object)
: NativeFunction(vm().names.Date, *global_object.function_prototype())
{
}
void DateConstructor::initialize(GlobalObject& global_object)
{
auto& vm = this->vm();
NativeFunction::initialize(global_object);
define_property(vm.names.prototype, global_object.date_prototype(), 0);
define_property(vm.names.length, Value(7), Attribute::Configurable);
define_native_function(vm.names.now, now, 0, Attribute::Writable | Attribute::Configurable);
define_native_function(vm.names.parse, parse, 1, Attribute::Writable | Attribute::Configurable);
define_native_function(vm.names.UTC, utc, 1, Attribute::Writable | Attribute::Configurable);
}
DateConstructor::~DateConstructor()
{
}
Value DateConstructor::call()
{
auto date = construct(*this);
if (!date.is_object())
return {};
return js_string(heap(), static_cast<Date&>(date.as_object()).string());
}
Value DateConstructor::construct(Function&)
{
if (vm().argument_count() == 0) {
struct timeval tv;
gettimeofday(&tv, nullptr);
auto datetime = Core::DateTime::now();
auto milliseconds = static_cast<u16>(tv.tv_usec / 1000);
return Date::create(global_object(), datetime, milliseconds);
}
if (vm().argument_count() == 1) {
auto value = vm().argument(0);
if (value.is_string())
value = parse_simplified_iso8601(value.as_string().string());
// A timestamp since the epoch, in UTC.
// FIXME: Date() probably should use a double as internal representation, so that NaN arguments and larger offsets are handled correctly.
double value_as_double = value.to_double(global_object());
auto datetime = Core::DateTime::from_timestamp(static_cast<time_t>(value_as_double / 1000));
auto milliseconds = static_cast<u16>(fmod(value_as_double, 1000));
return Date::create(global_object(), datetime, milliseconds);
}
// A date/time in components, in local time.
// FIXME: This doesn't construct an "Invalid Date" object if one of the parameters is NaN.
auto arg_or = [this](size_t i, i32 fallback) { return vm().argument_count() > i ? vm().argument(i).to_i32(global_object()) : fallback; };
int year = vm().argument(0).to_i32(global_object());
int month_index = vm().argument(1).to_i32(global_object());
int day = arg_or(2, 1);
int hours = arg_or(3, 0);
int minutes = arg_or(4, 0);
int seconds = arg_or(5, 0);
int milliseconds = arg_or(6, 0);
seconds += milliseconds / 1000;
milliseconds %= 1000;
if (milliseconds < 0) {
seconds -= 1;
milliseconds += 1000;
}
if (year >= 0 && year <= 99)
year += 1900;
int month = month_index + 1;
auto datetime = Core::DateTime::create(year, month, day, hours, minutes, seconds);
return Date::create(global_object(), datetime, milliseconds);
}
JS_DEFINE_NATIVE_FUNCTION(DateConstructor::now)
{
struct timeval tv;
gettimeofday(&tv, nullptr);
return Value(tv.tv_sec * 1000.0 + tv.tv_usec / 1000.0);
}
JS_DEFINE_NATIVE_FUNCTION(DateConstructor::parse)
{
if (!vm.argument_count())
return js_nan();
auto iso_8601 = vm.argument(0).to_string(global_object);
if (vm.exception())
return js_nan();
return parse_simplified_iso8601(iso_8601);
}
JS_DEFINE_NATIVE_FUNCTION(DateConstructor::utc)
{
auto arg_or = [&vm, &global_object](size_t i, i32 fallback) { return vm.argument_count() > i ? vm.argument(i).to_i32(global_object) : fallback; };
int year = vm.argument(0).to_i32(global_object);
if (year >= 0 && year <= 99)
year += 1900;
struct tm tm = {};
tm.tm_year = year - 1900;
tm.tm_mon = arg_or(1, 0); // 0-based in both tm and JavaScript
tm.tm_mday = arg_or(2, 1);
tm.tm_hour = arg_or(3, 0);
tm.tm_min = arg_or(4, 0);
tm.tm_sec = arg_or(5, 0);
// timegm() doesn't read tm.tm_wday and tm.tm_yday, no need to fill them in.
int milliseconds = arg_or(6, 0);
return Value(1000.0 * timegm(&tm) + milliseconds);
}
}

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* 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 DateConstructor final : public NativeFunction {
JS_OBJECT(DateConstructor, NativeFunction);
public:
explicit DateConstructor(GlobalObject&);
virtual void initialize(GlobalObject&) override;
virtual ~DateConstructor() override;
virtual Value call() override;
virtual Value construct(Function& new_target) override;
private:
virtual bool has_constructor() const override { return true; }
JS_DECLARE_NATIVE_FUNCTION(now);
JS_DECLARE_NATIVE_FUNCTION(parse);
JS_DECLARE_NATIVE_FUNCTION(utc);
};
}

View file

@ -0,0 +1,299 @@
/*
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* 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/String.h>
#include <LibCore/DateTime.h>
#include <LibJS/Runtime/Date.h>
#include <LibJS/Runtime/DatePrototype.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Value.h>
namespace JS {
static Date* typed_this(VM& vm, GlobalObject& global_object)
{
auto* this_object = vm.this_value(global_object).to_object(global_object);
if (!this_object)
return nullptr;
if (!is<Date>(this_object)) {
vm.throw_exception<TypeError>(global_object, ErrorType::NotA, "Date");
return nullptr;
}
return static_cast<Date*>(this_object);
}
DatePrototype::DatePrototype(GlobalObject& global_object)
: Object(*global_object.object_prototype())
{
}
void DatePrototype::initialize(GlobalObject& global_object)
{
auto& vm = this->vm();
Object::initialize(global_object);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(vm.names.getDate, get_date, 0, attr);
define_native_function(vm.names.getDay, get_day, 0, attr);
define_native_function(vm.names.getFullYear, get_full_year, 0, attr);
define_native_function(vm.names.getHours, get_hours, 0, attr);
define_native_function(vm.names.getMilliseconds, get_milliseconds, 0, attr);
define_native_function(vm.names.getMinutes, get_minutes, 0, attr);
define_native_function(vm.names.getMonth, get_month, 0, attr);
define_native_function(vm.names.getSeconds, get_seconds, 0, attr);
define_native_function(vm.names.getTime, get_time, 0, attr);
define_native_function(vm.names.getUTCDate, get_utc_date, 0, attr);
define_native_function(vm.names.getUTCDay, get_utc_day, 0, attr);
define_native_function(vm.names.getUTCFullYear, get_utc_full_year, 0, attr);
define_native_function(vm.names.getUTCHours, get_utc_hours, 0, attr);
define_native_function(vm.names.getUTCMilliseconds, get_utc_milliseconds, 0, attr);
define_native_function(vm.names.getUTCMinutes, get_utc_minutes, 0, attr);
define_native_function(vm.names.getUTCMonth, get_utc_month, 0, attr);
define_native_function(vm.names.getUTCSeconds, get_utc_seconds, 0, attr);
define_native_function(vm.names.toDateString, to_date_string, 0, attr);
define_native_function(vm.names.toISOString, to_iso_string, 0, attr);
define_native_function(vm.names.toLocaleDateString, to_locale_date_string, 0, attr);
define_native_function(vm.names.toLocaleString, to_locale_string, 0, attr);
define_native_function(vm.names.toLocaleTimeString, to_locale_time_string, 0, attr);
define_native_function(vm.names.toTimeString, to_time_string, 0, attr);
define_native_function(vm.names.toString, to_string, 0, attr);
// Aliases.
define_native_function(vm.names.valueOf, get_time, 0, attr);
// toJSON() isn't quite an alias for toISOString():
// - it returns null instead of throwing RangeError
// - its .length is 1, not 0
// - it can be transferred to other prototypes
}
DatePrototype::~DatePrototype()
{
}
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_date)
{
auto* this_object = typed_this(vm, global_object);
if (!this_object)
return {};
return Value(static_cast<double>(this_object->date()));
}
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_day)
{
auto* this_object = typed_this(vm, global_object);
if (!this_object)
return {};
return Value(static_cast<double>(this_object->day()));
}
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_full_year)
{
auto* this_object = typed_this(vm, global_object);
if (!this_object)
return {};
return Value(static_cast<double>(this_object->full_year()));
}
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_hours)
{
auto* this_object = typed_this(vm, global_object);
if (!this_object)
return {};
return Value(static_cast<double>(this_object->hours()));
}
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_milliseconds)
{
auto* this_object = typed_this(vm, global_object);
if (!this_object)
return {};
return Value(static_cast<double>(this_object->milliseconds()));
}
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_minutes)
{
auto* this_object = typed_this(vm, global_object);
if (!this_object)
return {};
return Value(static_cast<double>(this_object->minutes()));
}
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_month)
{
auto* this_object = typed_this(vm, global_object);
if (!this_object)
return {};
return Value(static_cast<double>(this_object->month()));
}
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_seconds)
{
auto* this_object = typed_this(vm, global_object);
if (!this_object)
return {};
return Value(static_cast<double>(this_object->seconds()));
}
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_time)
{
auto* this_object = typed_this(vm, global_object);
if (!this_object)
return {};
return Value(this_object->time());
}
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_utc_date)
{
auto* this_object = typed_this(vm, global_object);
if (!this_object)
return {};
return Value(static_cast<double>(this_object->utc_date()));
}
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_utc_day)
{
auto* this_object = typed_this(vm, global_object);
if (!this_object)
return {};
return Value(static_cast<double>(this_object->utc_day()));
}
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_utc_full_year)
{
auto* this_object = typed_this(vm, global_object);
if (!this_object)
return {};
return Value(static_cast<double>(this_object->utc_full_year()));
}
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_utc_hours)
{
auto* this_object = typed_this(vm, global_object);
if (!this_object)
return {};
return Value(static_cast<double>(this_object->utc_hours()));
}
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_utc_milliseconds)
{
auto* this_object = typed_this(vm, global_object);
if (!this_object)
return {};
return Value(static_cast<double>(this_object->utc_milliseconds()));
}
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_utc_month)
{
auto* this_object = typed_this(vm, global_object);
if (!this_object)
return {};
return Value(static_cast<double>(this_object->utc_month()));
}
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_utc_minutes)
{
auto* this_object = typed_this(vm, global_object);
if (!this_object)
return {};
return Value(static_cast<double>(this_object->utc_minutes()));
}
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_utc_seconds)
{
auto* this_object = typed_this(vm, global_object);
if (!this_object)
return {};
return Value(static_cast<double>(this_object->utc_seconds()));
}
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_date_string)
{
auto* this_object = typed_this(vm, global_object);
if (!this_object)
return {};
auto string = this_object->date_string();
return js_string(vm, move(string));
}
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_iso_string)
{
auto* this_object = typed_this(vm, global_object);
if (!this_object)
return {};
auto string = this_object->iso_date_string();
return js_string(vm, move(string));
}
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_locale_date_string)
{
auto* this_object = typed_this(vm, global_object);
if (!this_object)
return {};
// FIXME: Optional locales, options params.
auto string = this_object->locale_date_string();
return js_string(vm, move(string));
}
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_locale_string)
{
auto* this_object = typed_this(vm, global_object);
if (!this_object)
return {};
// FIXME: Optional locales, options params.
auto string = this_object->locale_string();
return js_string(vm, move(string));
}
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_locale_time_string)
{
auto* this_object = typed_this(vm, global_object);
if (!this_object)
return {};
// FIXME: Optional locales, options params.
auto string = this_object->locale_time_string();
return js_string(vm, move(string));
}
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_time_string)
{
auto* this_object = typed_this(vm, global_object);
if (!this_object)
return {};
auto string = this_object->time_string();
return js_string(vm, move(string));
}
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_string)
{
auto* this_object = typed_this(vm, global_object);
if (!this_object)
return {};
auto string = this_object->string();
return js_string(vm, move(string));
}
}

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* 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 DatePrototype final : public Object {
JS_OBJECT(DatePrototype, Object);
public:
explicit DatePrototype(GlobalObject&);
virtual void initialize(GlobalObject&) override;
virtual ~DatePrototype() override;
private:
JS_DECLARE_NATIVE_FUNCTION(get_date);
JS_DECLARE_NATIVE_FUNCTION(get_day);
JS_DECLARE_NATIVE_FUNCTION(get_full_year);
JS_DECLARE_NATIVE_FUNCTION(get_hours);
JS_DECLARE_NATIVE_FUNCTION(get_milliseconds);
JS_DECLARE_NATIVE_FUNCTION(get_minutes);
JS_DECLARE_NATIVE_FUNCTION(get_month);
JS_DECLARE_NATIVE_FUNCTION(get_seconds);
JS_DECLARE_NATIVE_FUNCTION(get_time);
JS_DECLARE_NATIVE_FUNCTION(get_utc_date);
JS_DECLARE_NATIVE_FUNCTION(get_utc_day);
JS_DECLARE_NATIVE_FUNCTION(get_utc_full_year);
JS_DECLARE_NATIVE_FUNCTION(get_utc_hours);
JS_DECLARE_NATIVE_FUNCTION(get_utc_milliseconds);
JS_DECLARE_NATIVE_FUNCTION(get_utc_minutes);
JS_DECLARE_NATIVE_FUNCTION(get_utc_month);
JS_DECLARE_NATIVE_FUNCTION(get_utc_seconds);
JS_DECLARE_NATIVE_FUNCTION(to_date_string);
JS_DECLARE_NATIVE_FUNCTION(to_iso_string);
JS_DECLARE_NATIVE_FUNCTION(to_locale_date_string);
JS_DECLARE_NATIVE_FUNCTION(to_locale_string);
JS_DECLARE_NATIVE_FUNCTION(to_locale_time_string);
JS_DECLARE_NATIVE_FUNCTION(to_time_string);
JS_DECLARE_NATIVE_FUNCTION(to_string);
};
}

View file

@ -0,0 +1,62 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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/Runtime/Error.h>
#include <LibJS/Runtime/GlobalObject.h>
namespace JS {
Error* Error::create(GlobalObject& global_object, const FlyString& name, const String& message)
{
return global_object.heap().allocate<Error>(global_object, name, message, *global_object.error_prototype());
}
Error::Error(const FlyString& name, const String& message, Object& prototype)
: Object(prototype)
, m_name(name)
, m_message(message)
{
}
Error::~Error()
{
}
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
ClassName* ClassName::create(GlobalObject& global_object, const String& message) \
{ \
return global_object.heap().allocate<ClassName>(global_object, message, *global_object.snake_name##_prototype()); \
} \
ClassName::ClassName(const String& message, Object& prototype) \
: Error(vm().names.ClassName, message, prototype) \
{ \
} \
ClassName::~ClassName() { }
JS_ENUMERATE_ERROR_SUBCLASSES
#undef __JS_ENUMERATE
}

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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/FlyString.h>
#include <LibJS/Runtime/Object.h>
namespace JS {
class Error : public Object {
JS_OBJECT(Error, Object);
public:
static Error* create(GlobalObject&, const FlyString& name, const String& message);
Error(const FlyString& name, const String& message, Object& prototype);
virtual ~Error() override;
const FlyString& name() const { return m_name; }
const String& message() const { return m_message; }
void set_name(const FlyString& name) { m_name = name; }
private:
FlyString m_name;
String m_message;
};
#define DECLARE_ERROR_SUBCLASS(ClassName, snake_name, PrototypeName, ConstructorName) \
class ClassName final : public Error { \
JS_OBJECT(ClassName, Error); \
\
public: \
static ClassName* create(GlobalObject&, const String& message); \
\
ClassName(const String& message, Object& prototype); \
virtual ~ClassName() override; \
};
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
DECLARE_ERROR_SUBCLASS(ClassName, snake_name, PrototypeName, ConstructorName)
JS_ENUMERATE_ERROR_SUBCLASSES
#undef __JS_ENUMERATE
}

View file

@ -0,0 +1,98 @@
/*
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* 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/Runtime/Error.h>
#include <LibJS/Runtime/ErrorConstructor.h>
#include <LibJS/Runtime/GlobalObject.h>
namespace JS {
ErrorConstructor::ErrorConstructor(GlobalObject& global_object)
: NativeFunction(vm().names.Error, *global_object.function_prototype())
{
}
void ErrorConstructor::initialize(GlobalObject& global_object)
{
auto& vm = this->vm();
NativeFunction::initialize(global_object);
define_property(vm.names.prototype, global_object.error_prototype(), 0);
define_property(vm.names.length, Value(1), Attribute::Configurable);
}
ErrorConstructor::~ErrorConstructor()
{
}
Value ErrorConstructor::call()
{
return construct(*this);
}
Value ErrorConstructor::construct(Function&)
{
auto& vm = this->vm();
String message = "";
if (!vm.call_frame().arguments.is_empty() && !vm.call_frame().arguments[0].is_undefined()) {
message = vm.call_frame().arguments[0].to_string(global_object());
if (vm.exception())
return {};
}
return Error::create(global_object(), vm.names.Error, message);
}
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
ConstructorName::ConstructorName(GlobalObject& global_object) \
: NativeFunction(*global_object.function_prototype()) \
{ \
} \
void ConstructorName::initialize(GlobalObject& global_object) \
{ \
auto& vm = this->vm(); \
NativeFunction::initialize(global_object); \
define_property(vm.names.prototype, global_object.snake_name##_prototype(), 0); \
define_property(vm.names.length, Value(1), Attribute::Configurable); \
} \
ConstructorName::~ConstructorName() { } \
Value ConstructorName::call() \
{ \
return construct(*this); \
} \
Value ConstructorName::construct(Function&) \
{ \
String message = ""; \
if (!vm().call_frame().arguments.is_empty() && !vm().call_frame().arguments[0].is_undefined()) { \
message = vm().call_frame().arguments[0].to_string(global_object()); \
if (vm().exception()) \
return {}; \
} \
return ClassName::create(global_object(), message); \
}
JS_ENUMERATE_ERROR_SUBCLASSES
#undef __JS_ENUMERATE
}

View file

@ -0,0 +1,69 @@
/*
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* 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/Error.h>
#include <LibJS/Runtime/NativeFunction.h>
namespace JS {
class ErrorConstructor final : public NativeFunction {
JS_OBJECT(ErrorConstructor, NativeFunction);
public:
explicit ErrorConstructor(GlobalObject&);
virtual void initialize(GlobalObject&) override;
virtual ~ErrorConstructor() override;
virtual Value call() override;
virtual Value construct(Function& new_target) override;
private:
virtual bool has_constructor() const override { return true; }
};
#define DECLARE_ERROR_SUBCLASS_CONSTRUCTOR(ClassName, snake_name, PrototypeName, ConstructorName) \
class ConstructorName final : public NativeFunction { \
JS_OBJECT(ConstructorName, NativeFunction); \
\
public: \
explicit ConstructorName(GlobalObject&); \
virtual void initialize(GlobalObject&) override; \
virtual ~ConstructorName() override; \
virtual Value call() override; \
virtual Value construct(Function& new_target) override; \
\
private: \
virtual bool has_constructor() const override { return true; } \
};
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
DECLARE_ERROR_SUBCLASS_CONSTRUCTOR(ClassName, snake_name, PrototypeName, ConstructorName)
JS_ENUMERATE_ERROR_SUBCLASSES
#undef __JS_ENUMERATE
}

View file

@ -0,0 +1,140 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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 <LibJS/Heap/Heap.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/ErrorPrototype.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/PrimitiveString.h>
#include <LibJS/Runtime/Value.h>
namespace JS {
ErrorPrototype::ErrorPrototype(GlobalObject& global_object)
: Object(*global_object.object_prototype())
{
}
void ErrorPrototype::initialize(GlobalObject& global_object)
{
auto& vm = this->vm();
Object::initialize(global_object);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_property(vm.names.name, name_getter, name_setter, attr);
define_native_property(vm.names.message, message_getter, {}, attr);
define_native_function(vm.names.toString, to_string, 0, attr);
}
ErrorPrototype::~ErrorPrototype()
{
}
JS_DEFINE_NATIVE_GETTER(ErrorPrototype::name_getter)
{
auto* this_object = vm.this_value(global_object).to_object(global_object);
if (!this_object)
return {};
if (!is<Error>(this_object)) {
vm.throw_exception<TypeError>(global_object, ErrorType::NotAn, "Error");
return {};
}
return js_string(vm, static_cast<const Error*>(this_object)->name());
}
JS_DEFINE_NATIVE_SETTER(ErrorPrototype::name_setter)
{
auto* this_object = vm.this_value(global_object).to_object(global_object);
if (!this_object)
return;
if (!is<Error>(this_object)) {
vm.throw_exception<TypeError>(global_object, ErrorType::NotAn, "Error");
return;
}
auto name = value.to_string(global_object);
if (vm.exception())
return;
static_cast<Error*>(this_object)->set_name(name);
}
JS_DEFINE_NATIVE_GETTER(ErrorPrototype::message_getter)
{
auto* this_object = vm.this_value(global_object).to_object(global_object);
if (!this_object)
return {};
if (!is<Error>(this_object)) {
vm.throw_exception<TypeError>(global_object, ErrorType::NotAn, "Error");
return {};
}
return js_string(vm, static_cast<const Error*>(this_object)->message());
}
JS_DEFINE_NATIVE_FUNCTION(ErrorPrototype::to_string)
{
if (!vm.this_value(global_object).is_object()) {
vm.throw_exception<TypeError>(global_object, ErrorType::NotAnObject, vm.this_value(global_object).to_string_without_side_effects());
return {};
}
auto& this_object = vm.this_value(global_object).as_object();
String name = "Error";
auto name_property = this_object.get(vm.names.name);
if (vm.exception())
return {};
if (!name_property.is_empty() && !name_property.is_undefined()) {
name = name_property.to_string(global_object);
if (vm.exception())
return {};
}
String message = "";
auto message_property = this_object.get(vm.names.message);
if (vm.exception())
return {};
if (!message_property.is_empty() && !message_property.is_undefined()) {
message = message_property.to_string(global_object);
if (vm.exception())
return {};
}
if (name.length() == 0)
return js_string(vm, message);
if (message.length() == 0)
return js_string(vm, name);
return js_string(vm, String::formatted("{}: {}", name, message));
}
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
PrototypeName::PrototypeName(GlobalObject& global_object) \
: Object(*global_object.error_prototype()) \
{ \
} \
PrototypeName::~PrototypeName() { }
JS_ENUMERATE_ERROR_SUBCLASSES
#undef __JS_ENUMERATE
}

View file

@ -0,0 +1,65 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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/Error.h>
namespace JS {
class ErrorPrototype final : public Object {
JS_OBJECT(ErrorPrototype, Object);
public:
explicit ErrorPrototype(GlobalObject&);
virtual void initialize(GlobalObject&) override;
virtual ~ErrorPrototype() override;
private:
JS_DECLARE_NATIVE_FUNCTION(to_string);
JS_DECLARE_NATIVE_GETTER(name_getter);
JS_DECLARE_NATIVE_SETTER(name_setter);
JS_DECLARE_NATIVE_GETTER(message_getter);
};
#define DECLARE_ERROR_SUBCLASS_PROTOTYPE(ClassName, snake_name, PrototypeName, ConstructorName) \
class PrototypeName final : public Object { \
JS_OBJECT(PrototypeName, Object); \
\
public: \
explicit PrototypeName(GlobalObject&); \
virtual void initialize(GlobalObject&) override { } \
virtual ~PrototypeName() override; \
};
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
DECLARE_ERROR_SUBCLASS_PROTOTYPE(ClassName, snake_name, PrototypeName, ConstructorName)
JS_ENUMERATE_ERROR_SUBCLASSES
#undef __JS_ENUMERATE
}

View file

@ -0,0 +1,36 @@
/*
* 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/Runtime/ErrorTypes.h>
namespace JS {
#define __ENUMERATE_JS_ERROR(name, message) \
const ErrorType ErrorType::name = ErrorType(message);
JS_ENUMERATE_ERROR_TYPES(__ENUMERATE_JS_ERROR)
#undef __ENUMERATE_JS_ERROR
}

View file

@ -0,0 +1,193 @@
/*
* 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
#define JS_ENUMERATE_ERROR_TYPES(M) \
M(ArrayMaxSize, "Maximum array size exceeded") \
M(ArrayPrototypeOneArg, "Array.prototype.{}() requires at least one argument") \
M(AccessorBadField, "Accessor descriptor's '{}' field must be a function or undefined") \
M(AccessorValueOrWritable, "Accessor property descriptor cannot specify a value or writable key") \
M(BigIntBadOperator, "Cannot use {} operator with BigInt") \
M(BigIntBadOperatorOtherType, "Cannot use {} operator with BigInt and other type") \
M(BigIntIntArgument, "BigInt argument must be an integer") \
M(BigIntInvalidValue, "Invalid value for BigInt: {}") \
M(ClassConstructorWithoutNew, "Class constructor {} must be called with 'new'") \
M(ClassIsAbstract, "Abstract class {} cannot be constructed directly") \
M(ClassDoesNotExtendAConstructorOrNull, "Class extends value {} is not a constructor or null") \
M(ConstructorWithoutNew, "{} constructor must be called with 'new'") \
M(Convert, "Cannot convert {} to {}") \
M(ConvertUndefinedToObject, "Cannot convert undefined to object") \
M(DescChangeNonConfigurable, "Cannot change attributes of non-configurable property '{}'") \
M(FunctionArgsNotObject, "Argument array must be an object") \
M(InOperatorWithObject, "'in' operator must be used on an object") \
M(InstanceOfOperatorBadPrototype, "'prototype' property of {} is not an object") \
M(InvalidAssignToConst, "Invalid assignment to const variable") \
M(InvalidIndex, "Index must be a positive integer") \
M(InvalidLeftHandAssignment, "Invalid left-hand side in assignment") \
M(InvalidLength, "Invalid {} length") \
M(InvalidRadix, "Radix must be an integer no less than 2, and no greater than 36") \
M(IsNotA, "{} is not a {}") \
M(IsNotAEvaluatedFrom, "{} is not a {} (evaluated from '{}')") \
M(IterableNextBadReturn, "iterator.next() returned a non-object value") \
M(IterableNextNotAFunction, "'next' property on returned object from Symbol.iterator method is " \
"not a function") \
M(JsonBigInt, "Cannot serialize BigInt value to JSON") \
M(JsonCircular, "Cannot stringify circular object") \
M(JsonMalformed, "Malformed JSON string") \
M(NotA, "Not a {} object") \
M(NotAConstructor, "{} is not a constructor") \
M(NotAFunction, "{} is not a function") \
M(NotAFunctionNoParam, "Not a function") \
M(NotAn, "Not an {} object") \
M(NotAnObject, "{} is not an object") \
M(NotASymbol, "{} is not a symbol") \
M(NotIterable, "{} is not iterable") \
M(NonExtensibleDefine, "Cannot define property {} on non-extensible object") \
M(NumberIncompatibleThis, "Number.prototype.{} method called with incompatible this target") \
M(ObjectDefinePropertyReturnedFalse, "Object's [[DefineProperty]] method returned false") \
M(ObjectSetPrototypeOfReturnedFalse, "Object's [[SetPrototypeOf]] method returned false") \
M(ObjectSetPrototypeOfTwoArgs, "Object.setPrototypeOf requires at least two arguments") \
M(ObjectPreventExtensionsReturnedFalse, "Object's [[PreventExtensions]] method returned false") \
M(ObjectPrototypeNullOrUndefinedOnSuperPropertyAccess, \
"Object prototype must not be {} on a super property access") \
M(ObjectPrototypeWrongType, "Prototype must be an object or null") \
M(ProxyConstructBadReturnType, "Proxy handler's construct trap violates invariant: must return " \
"an object") \
M(ProxyConstructorBadType, "Expected {} argument of Proxy constructor to be object, got {}") \
M(ProxyDefinePropExistingConfigurable, "Proxy handler's defineProperty trap violates " \
"invariant: a property cannot be defined as non-configurable if it already exists on the " \
"target object as a configurable property") \
M(ProxyDefinePropIncompatibleDescriptor, "Proxy handler's defineProperty trap violates " \
"invariant: the new descriptor is not compatible with the existing descriptor of the " \
"property on the target") \
M(ProxyDefinePropNonConfigurableNonExisting, "Proxy handler's defineProperty trap " \
"violates invariant: a property cannot be defined as non-configurable if it does not " \
"already exist on the target object") \
M(ProxyDefinePropNonExtensible, "Proxy handler's defineProperty trap violates invariant: " \
"a property cannot be reported as being defined if the property does not exist on " \
"the target and the target is non-extensible") \
M(ProxyDeleteNonConfigurable, "Proxy handler's deleteProperty trap violates invariant: " \
"cannot report a non-configurable own property of the target as deleted") \
M(ProxyGetImmutableDataProperty, "Proxy handler's get trap violates invariant: the " \
"returned value must match the value on the target if the property exists on the " \
"target as a non-writable, non-configurable own data property") \
M(ProxyGetNonConfigurableAccessor, "Proxy handler's get trap violates invariant: the " \
"returned value must be undefined if the property exists on the target as a " \
"non-configurable accessor property with an undefined get attribute") \
M(ProxyGetOwnDescriptorExistingConfigurable, "Proxy handler's getOwnPropertyDescriptor " \
"trap violates invariant: a property cannot be defined as non-configurable if it " \
"already exists on the target object as a configurable property") \
M(ProxyGetOwnDescriptorInvalidDescriptor, "Proxy handler's getOwnPropertyDescriptor trap " \
"violates invariant: invalid property descriptor for existing property on the target") \
M(ProxyGetOwnDescriptorInvalidNonConfig, "Proxy handler's getOwnPropertyDescriptor trap " \
"violates invariant: cannot report target's property as non-configurable if the " \
"property does not exist, or if it is configurable") \
M(ProxyGetOwnDescriptorNonConfigurable, "Proxy handler's getOwnPropertyDescriptor trap " \
"violates invariant: cannot return undefined for a property on the target which is " \
"a non-configurable property") \
M(ProxyGetOwnDescriptorReturn, "Proxy handler's getOwnPropertyDescriptor trap violates " \
"invariant: must return an object or undefined") \
M(ProxyGetOwnDescriptorUndefReturn, "Proxy handler's getOwnPropertyDescriptor trap " \
"violates invariant: cannot report a property as being undefined if it exists as an " \
"own property of the target and the target is non-extensible") \
M(ProxyGetPrototypeOfNonExtensible, "Proxy handler's getPrototypeOf trap violates " \
"invariant: cannot return a different prototype object for a non-extensible target") \
M(ProxyGetPrototypeOfReturn, "Proxy handler's getPrototypeOf trap violates invariant: " \
"must return an object or null") \
M(ProxyHasExistingNonConfigurable, "Proxy handler's has trap violates invariant: a " \
"property cannot be reported as non-existent if it exists on the target as a " \
"non-configurable property") \
M(ProxyHasExistingNonExtensible, "Proxy handler's has trap violates invariant: a property " \
"cannot be reported as non-existent if it exists on the target and the target is " \
"non-extensible") \
M(ProxyInvalidTrap, "Proxy handler's {} trap wasn't undefined, null, or callable") \
M(ProxyIsExtensibleReturn, "Proxy handler's isExtensible trap violates invariant: " \
"return value must match the target's extensibility") \
M(ProxyPreventExtensionsReturn, "Proxy handler's preventExtensions trap violates " \
"invariant: cannot return true if the target object is extensible") \
M(ProxyRevoked, "An operation was performed on a revoked Proxy object") \
M(ProxySetImmutableDataProperty, "Proxy handler's set trap violates invariant: cannot " \
"return true for a property on the target which is a non-configurable, non-writable " \
"own data property") \
M(ProxySetNonConfigurableAccessor, "Proxy handler's set trap violates invariant: cannot " \
"return true for a property on the target which is a non-configurable own accessor " \
"property with an undefined set attribute") \
M(ProxySetPrototypeOfNonExtensible, "Proxy handler's setPrototypeOf trap violates " \
"invariant: the argument must match the prototype of the target if the " \
"target is non-extensible") \
M(ProxyTwoArguments, "Proxy constructor requires at least two arguments") \
M(ReduceNoInitial, "Reduce of empty array with no initial value") \
M(ReferencePrimitiveAssignment, "Cannot assign property {} to primitive value") \
M(ReferenceUnresolvable, "Unresolvable reference") \
M(ReflectArgumentMustBeAFunction, "First argument of Reflect.{}() must be a function") \
M(ReflectArgumentMustBeAnObject, "First argument of Reflect.{}() must be an object") \
M(ReflectBadArgumentsList, "Arguments list must be an object") \
M(ReflectBadNewTarget, "Optional third argument of Reflect.construct() must be a constructor") \
M(ReflectBadDescriptorArgument, "Descriptor argument is not an object") \
M(RegExpCompileError, "RegExp compile error: {}") \
M(RegExpObjectBadFlag, "Invalid RegExp flag '{}'") \
M(RegExpObjectRepeatedFlag, "Repeated RegExp flag '{}'") \
M(StringRawCannotConvert, "Cannot convert property 'raw' to object from {}") \
M(StringRepeatCountMustBe, "repeat count must be a {} number") \
M(ThisHasNotBeenInitialized, "|this| has not been initialized") \
M(ThisIsAlreadyInitialized, "|this| is already initialized") \
M(ToObjectNullOrUndef, "ToObject on null or undefined") \
M(TypedArrayInvalidBufferLength, "Invalid buffer length for {}: must be a multiple of {}, got {}") \
M(TypedArrayInvalidByteOffset, "Invalid byte offset for {}: must be a multiple of {}, got {}") \
M(TypedArrayOutOfRangeByteOffset, "Typed array byte offset {} is out of range for buffer with length {}") \
M(TypedArrayOutOfRangeByteOffsetOrLength, "Typed array range {}:{} is out of range for buffer with length {}") \
M(UnknownIdentifier, "'{}' is not defined") \
/* LibWeb bindings */ \
M(NotAByteString, "Argument to {}() must be a byte string") \
M(BadArgCountOne, "{}() needs one argument") \
M(BadArgCountAtLeastOne, "{}() needs at least one argument") \
M(BadArgCountMany, "{}() needs {} arguments")
namespace JS {
class ErrorType {
public:
#define __ENUMERATE_JS_ERROR(name, message) \
static const ErrorType name;
JS_ENUMERATE_ERROR_TYPES(__ENUMERATE_JS_ERROR)
#undef __ENUMERATE_JS_ERROR
const char* message() const
{
return m_message;
}
private:
explicit ErrorType(const char* message)
: m_message(message)
{
}
const char* m_message;
};
}

View file

@ -0,0 +1,63 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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/String.h>
#include <LibJS/AST.h>
#include <LibJS/Runtime/Exception.h>
#include <LibJS/Runtime/VM.h>
namespace JS {
Exception::Exception(Value value)
: m_value(value)
{
auto& call_stack = vm().call_stack();
for (ssize_t i = call_stack.size() - 1; i >= 0; --i) {
String function_name = call_stack[i]->function_name;
if (function_name.is_empty())
function_name = "<anonymous>";
m_trace.append(function_name);
}
auto& node_stack = vm().node_stack();
for (ssize_t i = node_stack.size() - 1; i >= 0; --i) {
auto* node = node_stack[i];
ASSERT(node);
m_source_ranges.append(node->source_range());
}
}
Exception::~Exception()
{
}
void Exception::visit_edges(Visitor& visitor)
{
Cell::visit_edges(visitor);
visitor.visit(m_value);
}
}

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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/Vector.h>
#include <LibJS/Runtime/Cell.h>
#include <LibJS/Runtime/Value.h>
#include <LibJS/SourceRange.h>
namespace JS {
class Exception : public Cell {
public:
explicit Exception(Value);
virtual ~Exception() override;
Value value() const { return m_value; }
const Vector<String>& trace() const { return m_trace; }
const Vector<SourceRange>& source_ranges() const { return m_source_ranges; }
private:
virtual const char* class_name() const override { return "Exception"; }
virtual void visit_edges(Visitor&) override;
Value m_value;
Vector<String> m_trace;
Vector<SourceRange> m_source_ranges;
};
}

View file

@ -0,0 +1,99 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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/BoundFunction.h>
#include <LibJS/Runtime/Function.h>
#include <LibJS/Runtime/GlobalObject.h>
namespace JS {
Function::Function(Object& prototype)
: Function(prototype, {}, {})
{
}
Function::Function(Object& prototype, Value bound_this, Vector<Value> bound_arguments)
: Object(prototype)
, m_bound_this(bound_this)
, m_bound_arguments(move(bound_arguments))
{
}
Function::~Function()
{
}
BoundFunction* Function::bind(Value bound_this_value, Vector<Value> arguments)
{
auto& vm = this->vm();
Function& target_function = is<BoundFunction>(*this) ? static_cast<BoundFunction&>(*this).target_function() : *this;
auto bound_this_object = [&vm, bound_this_value, this]() -> Value {
if (!m_bound_this.is_empty())
return m_bound_this;
switch (bound_this_value.type()) {
case Value::Type::Undefined:
case Value::Type::Null:
if (vm.in_strict_mode())
return bound_this_value;
return &global_object();
default:
return bound_this_value.to_object(global_object());
}
}();
i32 computed_length = 0;
auto length_property = get(vm.names.length);
if (vm.exception())
return nullptr;
if (length_property.is_number())
computed_length = max(0, length_property.as_i32() - static_cast<i32>(arguments.size()));
Object* constructor_prototype = nullptr;
auto prototype_property = target_function.get(vm.names.prototype);
if (vm.exception())
return nullptr;
if (prototype_property.is_object())
constructor_prototype = &prototype_property.as_object();
auto all_bound_arguments = bound_arguments();
all_bound_arguments.append(move(arguments));
return heap().allocate<BoundFunction>(global_object(), global_object(), target_function, bound_this_object, move(all_bound_arguments), computed_length, constructor_prototype);
}
void Function::visit_edges(Visitor& visitor)
{
Object::visit_edges(visitor);
visitor.visit(m_bound_this);
for (auto argument : m_bound_arguments)
visitor.visit(argument);
}
}

View file

@ -0,0 +1,79 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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/Object.h>
namespace JS {
class Function : public Object {
JS_OBJECT(Function, Object);
public:
enum class ConstructorKind {
Base,
Derived,
};
virtual ~Function();
virtual void initialize(GlobalObject&) override { }
virtual Value call() = 0;
virtual Value construct(Function& new_target) = 0;
virtual const FlyString& name() const = 0;
virtual LexicalEnvironment* create_environment() = 0;
virtual void visit_edges(Visitor&) override;
BoundFunction* bind(Value bound_this_value, Vector<Value> arguments);
Value bound_this() const { return m_bound_this; }
const Vector<Value>& bound_arguments() const { return m_bound_arguments; }
Value home_object() const { return m_home_object; }
void set_home_object(Value home_object) { m_home_object = home_object; }
ConstructorKind constructor_kind() const { return m_constructor_kind; };
void set_constructor_kind(ConstructorKind constructor_kind) { m_constructor_kind = constructor_kind; }
virtual bool is_strict_mode() const { return false; }
protected:
explicit Function(Object& prototype);
Function(Object& prototype, Value bound_this, Vector<Value> bound_arguments);
private:
virtual bool is_function() const override { return true; }
Value m_bound_this;
Vector<Value> m_bound_arguments;
Value m_home_object;
ConstructorKind m_constructor_kind = ConstructorKind::Base;
};
}

View file

@ -0,0 +1,105 @@
/*
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* 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/StringBuilder.h>
#include <LibJS/AST.h>
#include <LibJS/Interpreter.h>
#include <LibJS/Parser.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/FunctionConstructor.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/ScriptFunction.h>
namespace JS {
FunctionConstructor::FunctionConstructor(GlobalObject& global_object)
: NativeFunction(vm().names.Function, *global_object.function_prototype())
{
}
void FunctionConstructor::initialize(GlobalObject& global_object)
{
auto& vm = this->vm();
NativeFunction::initialize(global_object);
define_property(vm.names.prototype, global_object.function_prototype(), 0);
define_property(vm.names.length, Value(1), Attribute::Configurable);
}
FunctionConstructor::~FunctionConstructor()
{
}
Value FunctionConstructor::call()
{
return construct(*this);
}
Value FunctionConstructor::construct(Function&)
{
auto& vm = this->vm();
String parameters_source = "";
String body_source = "";
if (vm.argument_count() == 1) {
body_source = vm.argument(0).to_string(global_object());
if (vm.exception())
return {};
}
if (vm.argument_count() > 1) {
Vector<String> parameters;
for (size_t i = 0; i < vm.argument_count() - 1; ++i) {
parameters.append(vm.argument(i).to_string(global_object()));
if (vm.exception())
return {};
}
StringBuilder parameters_builder;
parameters_builder.join(',', parameters);
parameters_source = parameters_builder.build();
body_source = vm.argument(vm.argument_count() - 1).to_string(global_object());
if (vm.exception())
return {};
}
auto source = String::formatted("function anonymous({}\n) {{\n{}\n}}", parameters_source, body_source);
auto parser = Parser(Lexer(source));
auto function_expression = parser.parse_function_node<FunctionExpression>();
if (parser.has_errors()) {
auto error = parser.errors()[0];
vm.throw_exception<SyntaxError>(global_object(), error.to_string());
return {};
}
OwnPtr<Interpreter> local_interpreter;
Interpreter* interpreter = vm.interpreter_if_exists();
if (!interpreter) {
local_interpreter = Interpreter::create_with_existing_global_object(global_object());
interpreter = local_interpreter.ptr();
}
VM::InterpreterExecutionScope scope(*interpreter);
return function_expression->execute(*interpreter, global_object());
}
}

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* 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 FunctionConstructor final : public NativeFunction {
JS_OBJECT(FunctionConstructor, NativeFunction);
public:
explicit FunctionConstructor(GlobalObject&);
virtual void initialize(GlobalObject&) override;
virtual ~FunctionConstructor() override;
virtual Value call() override;
virtual Value construct(Function& new_target) override;
private:
virtual bool has_constructor() const override { return true; }
};
}

View file

@ -0,0 +1,189 @@
/*
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* 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/AST.h>
#include <LibJS/Interpreter.h>
#include <LibJS/Runtime/BoundFunction.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/Function.h>
#include <LibJS/Runtime/FunctionPrototype.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/MarkedValueList.h>
#include <LibJS/Runtime/NativeFunction.h>
#include <LibJS/Runtime/ScriptFunction.h>
namespace JS {
FunctionPrototype::FunctionPrototype(GlobalObject& global_object)
: Object(*global_object.object_prototype())
{
}
void FunctionPrototype::initialize(GlobalObject& global_object)
{
auto& vm = this->vm();
Object::initialize(global_object);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(vm.names.apply, apply, 2, attr);
define_native_function(vm.names.bind, bind, 1, attr);
define_native_function(vm.names.call, call, 1, attr);
define_native_function(vm.names.toString, to_string, 0, attr);
define_native_function(vm.well_known_symbol_has_instance(), symbol_has_instance, 1, 0);
define_property(vm.names.length, Value(0), Attribute::Configurable);
define_property(vm.names.name, js_string(heap(), ""), Attribute::Configurable);
}
FunctionPrototype::~FunctionPrototype()
{
}
JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::apply)
{
auto* this_object = vm.this_value(global_object).to_object(global_object);
if (!this_object)
return {};
if (!this_object->is_function()) {
vm.throw_exception<TypeError>(global_object, ErrorType::NotA, "Function");
return {};
}
auto& function = static_cast<Function&>(*this_object);
auto this_arg = vm.argument(0);
auto arg_array = vm.argument(1);
if (arg_array.is_nullish())
return vm.call(function, this_arg);
if (!arg_array.is_object()) {
vm.throw_exception<TypeError>(global_object, ErrorType::FunctionArgsNotObject);
return {};
}
auto length = length_of_array_like(global_object, arg_array.as_object());
if (vm.exception())
return {};
MarkedValueList arguments(vm.heap());
for (size_t i = 0; i < length; ++i) {
auto element = arg_array.as_object().get(i);
if (vm.exception())
return {};
arguments.append(element.value_or(js_undefined()));
}
return vm.call(function, this_arg, move(arguments));
}
JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::bind)
{
auto* this_object = vm.this_value(global_object).to_object(global_object);
if (!this_object)
return {};
if (!this_object->is_function()) {
vm.throw_exception<TypeError>(global_object, ErrorType::NotA, "Function");
return {};
}
auto& this_function = static_cast<Function&>(*this_object);
auto bound_this_arg = vm.argument(0);
Vector<Value> arguments;
if (vm.argument_count() > 1) {
arguments = vm.call_frame().arguments;
arguments.remove(0);
}
return this_function.bind(bound_this_arg, move(arguments));
}
JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::call)
{
auto* this_object = vm.this_value(global_object).to_object(global_object);
if (!this_object)
return {};
if (!this_object->is_function()) {
vm.throw_exception<TypeError>(global_object, ErrorType::NotA, "Function");
return {};
}
auto& function = static_cast<Function&>(*this_object);
auto this_arg = vm.argument(0);
MarkedValueList arguments(vm.heap());
if (vm.argument_count() > 1) {
for (size_t i = 1; i < vm.argument_count(); ++i)
arguments.append(vm.argument(i));
}
return vm.call(function, this_arg, move(arguments));
}
JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::to_string)
{
auto* this_object = vm.this_value(global_object).to_object(global_object);
if (!this_object)
return {};
if (!this_object->is_function()) {
vm.throw_exception<TypeError>(global_object, ErrorType::NotA, "Function");
return {};
}
String function_name = static_cast<Function*>(this_object)->name();
String function_parameters = "";
String function_body;
if (is<NativeFunction>(this_object) || is<BoundFunction>(this_object)) {
function_body = String::formatted(" [{}]", this_object->class_name());
} else {
StringBuilder parameters_builder;
auto first = true;
for (auto& parameter : static_cast<ScriptFunction*>(this_object)->parameters()) {
if (!first)
parameters_builder.append(", ");
first = false;
parameters_builder.append(parameter.name);
if (parameter.default_value) {
// FIXME: See note below
parameters_builder.append(" = TODO");
}
}
function_parameters = parameters_builder.build();
// FIXME: ASTNodes should be able to dump themselves to source strings - something like this:
// auto& body = static_cast<ScriptFunction*>(this_object)->body();
// function_body = body.to_source();
function_body = " ???";
}
auto function_source = String::formatted(
"function {}({}) {{\n{}\n}}",
function_name.is_null() ? "" : function_name, function_parameters, function_body);
return js_string(vm, function_source);
}
JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::symbol_has_instance)
{
auto* this_object = vm.this_value(global_object).to_object(global_object);
if (!this_object)
return {};
if (!this_object->is_function()) {
vm.throw_exception<TypeError>(global_object, ErrorType::NotA, "Function");
return {};
}
return ordinary_has_instance(global_object, vm.argument(0), this_object);
}
}

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* 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 FunctionPrototype final : public Object {
JS_OBJECT(FunctionPrototype, Object);
public:
explicit FunctionPrototype(GlobalObject&);
virtual void initialize(GlobalObject&) override;
virtual ~FunctionPrototype() override;
private:
JS_DECLARE_NATIVE_FUNCTION(apply);
JS_DECLARE_NATIVE_FUNCTION(bind);
JS_DECLARE_NATIVE_FUNCTION(call);
JS_DECLARE_NATIVE_FUNCTION(to_string);
JS_DECLARE_NATIVE_FUNCTION(symbol_has_instance);
};
}

View file

@ -0,0 +1,310 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* 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/Utf8View.h>
#include <LibJS/Console.h>
#include <LibJS/Heap/DeferGC.h>
#include <LibJS/Runtime/ArrayBufferConstructor.h>
#include <LibJS/Runtime/ArrayBufferPrototype.h>
#include <LibJS/Runtime/ArrayConstructor.h>
#include <LibJS/Runtime/ArrayIteratorPrototype.h>
#include <LibJS/Runtime/ArrayPrototype.h>
#include <LibJS/Runtime/BigIntConstructor.h>
#include <LibJS/Runtime/BigIntPrototype.h>
#include <LibJS/Runtime/BooleanConstructor.h>
#include <LibJS/Runtime/BooleanPrototype.h>
#include <LibJS/Runtime/ConsoleObject.h>
#include <LibJS/Runtime/DateConstructor.h>
#include <LibJS/Runtime/DatePrototype.h>
#include <LibJS/Runtime/ErrorConstructor.h>
#include <LibJS/Runtime/ErrorPrototype.h>
#include <LibJS/Runtime/FunctionConstructor.h>
#include <LibJS/Runtime/FunctionPrototype.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/IteratorPrototype.h>
#include <LibJS/Runtime/JSONObject.h>
#include <LibJS/Runtime/MathObject.h>
#include <LibJS/Runtime/NativeFunction.h>
#include <LibJS/Runtime/NumberConstructor.h>
#include <LibJS/Runtime/NumberPrototype.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/ObjectConstructor.h>
#include <LibJS/Runtime/ObjectPrototype.h>
#include <LibJS/Runtime/ProxyConstructor.h>
#include <LibJS/Runtime/ReflectObject.h>
#include <LibJS/Runtime/RegExpConstructor.h>
#include <LibJS/Runtime/RegExpPrototype.h>
#include <LibJS/Runtime/Shape.h>
#include <LibJS/Runtime/StringConstructor.h>
#include <LibJS/Runtime/StringIteratorPrototype.h>
#include <LibJS/Runtime/StringPrototype.h>
#include <LibJS/Runtime/SymbolConstructor.h>
#include <LibJS/Runtime/SymbolPrototype.h>
#include <LibJS/Runtime/TypedArray.h>
#include <LibJS/Runtime/TypedArrayConstructor.h>
#include <LibJS/Runtime/TypedArrayPrototype.h>
#include <LibJS/Runtime/Value.h>
#include <ctype.h>
namespace JS {
GlobalObject::GlobalObject()
: ScopeObject(GlobalObjectTag::Tag)
, m_console(make<Console>(*this))
{
}
void GlobalObject::initialize()
{
auto& vm = this->vm();
ensure_shape_is_unique();
// These are done first since other prototypes depend on their presence.
m_empty_object_shape = heap().allocate_without_global_object<Shape>(*this);
m_object_prototype = heap().allocate_without_global_object<ObjectPrototype>(*this);
m_function_prototype = heap().allocate_without_global_object<FunctionPrototype>(*this);
m_new_object_shape = vm.heap().allocate_without_global_object<Shape>(*this);
m_new_object_shape->set_prototype_without_transition(m_object_prototype);
m_new_script_function_prototype_object_shape = vm.heap().allocate_without_global_object<Shape>(*this);
m_new_script_function_prototype_object_shape->set_prototype_without_transition(m_object_prototype);
m_new_script_function_prototype_object_shape->add_property_without_transition(vm.names.constructor, Attribute::Writable | Attribute::Configurable);
static_cast<FunctionPrototype*>(m_function_prototype)->initialize(*this);
static_cast<ObjectPrototype*>(m_object_prototype)->initialize(*this);
set_prototype(m_object_prototype);
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
if (!m_##snake_name##_prototype) \
m_##snake_name##_prototype = heap().allocate<PrototypeName>(*this, *this);
JS_ENUMERATE_BUILTIN_TYPES
#undef __JS_ENUMERATE
#define __JS_ENUMERATE(ClassName, snake_name) \
if (!m_##snake_name##_prototype) \
m_##snake_name##_prototype = heap().allocate<ClassName##Prototype>(*this, *this);
JS_ENUMERATE_ITERATOR_PROTOTYPES
#undef __JS_ENUMERATE
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(vm.names.gc, gc, 0, attr);
define_native_function(vm.names.isNaN, is_nan, 1, attr);
define_native_function(vm.names.isFinite, is_finite, 1, attr);
define_native_function(vm.names.parseFloat, parse_float, 1, attr);
define_native_function(vm.names.parseInt, parse_int, 1, attr);
define_property(vm.names.NaN, js_nan(), 0);
define_property(vm.names.Infinity, js_infinity(), 0);
define_property(vm.names.undefined, js_undefined(), 0);
define_property(vm.names.globalThis, this, attr);
define_property(vm.names.console, heap().allocate<ConsoleObject>(*this, *this), attr);
define_property(vm.names.Math, heap().allocate<MathObject>(*this, *this), attr);
define_property(vm.names.JSON, heap().allocate<JSONObject>(*this, *this), attr);
define_property(vm.names.Reflect, heap().allocate<ReflectObject>(*this, *this), attr);
add_constructor(vm.names.Array, m_array_constructor, m_array_prototype);
add_constructor(vm.names.ArrayBuffer, m_array_buffer_constructor, m_array_buffer_prototype);
add_constructor(vm.names.BigInt, m_bigint_constructor, m_bigint_prototype);
add_constructor(vm.names.Boolean, m_boolean_constructor, m_boolean_prototype);
add_constructor(vm.names.Date, m_date_constructor, m_date_prototype);
add_constructor(vm.names.Error, m_error_constructor, m_error_prototype);
add_constructor(vm.names.Function, m_function_constructor, m_function_prototype);
add_constructor(vm.names.Number, m_number_constructor, m_number_prototype);
add_constructor(vm.names.Object, m_object_constructor, m_object_prototype);
add_constructor(vm.names.Proxy, m_proxy_constructor, nullptr);
add_constructor(vm.names.RegExp, m_regexp_constructor, m_regexp_prototype);
add_constructor(vm.names.String, m_string_constructor, m_string_prototype);
add_constructor(vm.names.Symbol, m_symbol_constructor, m_symbol_prototype);
initialize_constructor(vm.names.TypedArray, m_typed_array_constructor, m_typed_array_prototype);
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
add_constructor(vm.names.ClassName, m_##snake_name##_constructor, m_##snake_name##_prototype);
JS_ENUMERATE_ERROR_SUBCLASSES
JS_ENUMERATE_TYPED_ARRAYS
#undef __JS_ENUMERATE
}
GlobalObject::~GlobalObject()
{
}
void GlobalObject::visit_edges(Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_empty_object_shape);
visitor.visit(m_new_object_shape);
visitor.visit(m_new_script_function_prototype_object_shape);
visitor.visit(m_proxy_constructor);
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
visitor.visit(m_##snake_name##_constructor);
JS_ENUMERATE_ERROR_SUBCLASSES
#undef __JS_ENUMERATE
#define __JS_ENUMERATE(ClassName, snake_name) \
visitor.visit(m_##snake_name##_prototype);
JS_ENUMERATE_ITERATOR_PROTOTYPES
#undef __JS_ENUMERATE
}
JS_DEFINE_NATIVE_FUNCTION(GlobalObject::gc)
{
dbgln("Forced garbage collection requested!");
vm.heap().collect_garbage();
return js_undefined();
}
JS_DEFINE_NATIVE_FUNCTION(GlobalObject::is_nan)
{
auto number = vm.argument(0).to_number(global_object);
if (vm.exception())
return {};
return Value(number.is_nan());
}
JS_DEFINE_NATIVE_FUNCTION(GlobalObject::is_finite)
{
auto number = vm.argument(0).to_number(global_object);
if (vm.exception())
return {};
return Value(number.is_finite_number());
}
JS_DEFINE_NATIVE_FUNCTION(GlobalObject::parse_float)
{
if (vm.argument(0).is_number())
return vm.argument(0);
auto string = vm.argument(0).to_string(global_object);
if (vm.exception())
return {};
for (size_t length = string.length(); length > 0; --length) {
// This can't throw, so no exception check is fine.
auto number = Value(js_string(vm, string.substring(0, length))).to_number(global_object);
if (!number.is_nan())
return number;
}
return js_nan();
}
JS_DEFINE_NATIVE_FUNCTION(GlobalObject::parse_int)
{
// 18.2.5 parseInt ( string, radix )
auto input_string = vm.argument(0).to_string(global_object);
if (vm.exception())
return {};
// FIXME: There's a bunch of unnecessary string copying here.
double sign = 1;
auto s = input_string.trim_whitespace(TrimMode::Left);
if (!s.is_empty() && s[0] == '-')
sign = -1;
if (!s.is_empty() && (s[0] == '+' || s[0] == '-'))
s = s.substring(1, s.length() - 1);
auto radix = vm.argument(1).to_i32(global_object);
if (vm.exception())
return {};
bool strip_prefix = true;
if (radix != 0) {
if (radix < 2 || radix > 36)
return js_nan();
if (radix != 16)
strip_prefix = false;
} else {
radix = 10;
}
if (strip_prefix) {
if (s.length() >= 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
s = s.substring(2, s.length() - 2);
radix = 16;
}
}
auto parse_digit = [&](u32 codepoint, i32 radix) -> Optional<i32> {
i32 digit = -1;
if (isdigit(codepoint))
digit = codepoint - '0';
else if (islower(codepoint))
digit = 10 + (codepoint - 'a');
else if (isupper(codepoint))
digit = 10 + (codepoint - 'A');
if (digit == -1 || digit >= radix)
return {};
return digit;
};
bool had_digits = false;
double number = 0;
for (auto codepoint : Utf8View(s)) {
auto digit = parse_digit(codepoint, radix);
if (!digit.has_value())
break;
had_digits = true;
number *= radix;
number += digit.value();
}
if (!had_digits)
return js_nan();
return Value(sign * number);
}
Optional<Variable> GlobalObject::get_from_scope(const FlyString& name) const
{
auto value = get(name);
if (value.is_empty())
return {};
return Variable { value, DeclarationKind::Var };
}
void GlobalObject::put_to_scope(const FlyString& name, Variable variable)
{
put(name, variable.value);
}
bool GlobalObject::has_this_binding() const
{
return true;
}
Value GlobalObject::get_this_binding(GlobalObject&) const
{
return Value(this);
}
}

View file

@ -0,0 +1,133 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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/Heap/Heap.h>
#include <LibJS/Runtime/ScopeObject.h>
#include <LibJS/Runtime/VM.h>
namespace JS {
class GlobalObject : public ScopeObject {
JS_OBJECT(GlobalObject, ScopeObject);
public:
explicit GlobalObject();
virtual void initialize();
virtual ~GlobalObject() override;
virtual Optional<Variable> get_from_scope(const FlyString&) const override;
virtual void put_to_scope(const FlyString&, Variable) override;
virtual bool has_this_binding() const override;
virtual Value get_this_binding(GlobalObject&) const override;
Console& console() { return *m_console; }
Shape* empty_object_shape() { return m_empty_object_shape; }
Shape* new_object_shape() { return m_new_object_shape; }
Shape* new_script_function_prototype_object_shape() { return m_new_script_function_prototype_object_shape; }
// Not included in JS_ENUMERATE_NATIVE_OBJECTS due to missing distinct prototype
ProxyConstructor* proxy_constructor() { return m_proxy_constructor; }
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
ConstructorName* snake_name##_constructor() { return m_##snake_name##_constructor; } \
Object* snake_name##_prototype() { return m_##snake_name##_prototype; }
JS_ENUMERATE_BUILTIN_TYPES
#undef __JS_ENUMERATE
#define __JS_ENUMERATE(ClassName, snake_name) \
Object* snake_name##_prototype() { return m_##snake_name##_prototype; }
JS_ENUMERATE_ITERATOR_PROTOTYPES
#undef __JS_ENUMERATE
protected:
virtual void visit_edges(Visitor&) override;
template<typename ConstructorType>
void initialize_constructor(const FlyString& property_name, ConstructorType*&, Object* prototype);
template<typename ConstructorType>
void add_constructor(const FlyString& property_name, ConstructorType*&, Object* prototype);
private:
JS_DECLARE_NATIVE_FUNCTION(gc);
JS_DECLARE_NATIVE_FUNCTION(is_nan);
JS_DECLARE_NATIVE_FUNCTION(is_finite);
JS_DECLARE_NATIVE_FUNCTION(parse_float);
JS_DECLARE_NATIVE_FUNCTION(parse_int);
NonnullOwnPtr<Console> m_console;
Shape* m_empty_object_shape { nullptr };
Shape* m_new_object_shape { nullptr };
Shape* m_new_script_function_prototype_object_shape { nullptr };
// Not included in JS_ENUMERATE_NATIVE_OBJECTS due to missing distinct prototype
ProxyConstructor* m_proxy_constructor { nullptr };
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
ConstructorName* m_##snake_name##_constructor { nullptr }; \
Object* m_##snake_name##_prototype { nullptr };
JS_ENUMERATE_BUILTIN_TYPES
#undef __JS_ENUMERATE
#define __JS_ENUMERATE(ClassName, snake_name) \
Object* m_##snake_name##_prototype { nullptr };
JS_ENUMERATE_ITERATOR_PROTOTYPES
#undef __JS_ENUMERATE
};
template<typename ConstructorType>
inline void GlobalObject::initialize_constructor(const FlyString& property_name, ConstructorType*& constructor, Object* prototype)
{
auto& vm = this->vm();
constructor = heap().allocate<ConstructorType>(*this, *this);
constructor->define_property(vm.names.name, js_string(heap(), property_name), Attribute::Configurable);
if (vm.exception())
return;
if (prototype) {
prototype->define_property(vm.names.constructor, constructor, Attribute::Writable | Attribute::Configurable);
if (vm.exception())
return;
}
}
template<typename ConstructorType>
inline void GlobalObject::add_constructor(const FlyString& property_name, ConstructorType*& constructor, Object* prototype)
{
initialize_constructor(property_name, constructor, prototype);
define_property(property_name, constructor, Attribute::Writable | Attribute::Configurable);
}
inline GlobalObject* Shape::global_object() const
{
return static_cast<GlobalObject*>(m_global_object);
}
}

View file

@ -0,0 +1,396 @@
/*
* 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/QuickSort.h>
#include <LibJS/Runtime/Accessor.h>
#include <LibJS/Runtime/IndexedProperties.h>
namespace JS {
SimpleIndexedPropertyStorage::SimpleIndexedPropertyStorage(Vector<Value>&& initial_values)
: m_array_size(initial_values.size())
, m_packed_elements(move(initial_values))
{
}
bool SimpleIndexedPropertyStorage::has_index(u32 index) const
{
return index < m_array_size && !m_packed_elements[index].is_empty();
}
Optional<ValueAndAttributes> SimpleIndexedPropertyStorage::get(u32 index) const
{
if (index >= m_array_size)
return {};
return ValueAndAttributes { m_packed_elements[index], default_attributes };
}
void SimpleIndexedPropertyStorage::put(u32 index, Value value, PropertyAttributes attributes)
{
ASSERT(attributes == default_attributes);
ASSERT(index < SPARSE_ARRAY_THRESHOLD);
if (index >= m_array_size) {
m_array_size = index + 1;
if (index >= m_packed_elements.size())
m_packed_elements.resize(index + MIN_PACKED_RESIZE_AMOUNT >= SPARSE_ARRAY_THRESHOLD ? SPARSE_ARRAY_THRESHOLD : index + MIN_PACKED_RESIZE_AMOUNT);
}
m_packed_elements[index] = value;
}
void SimpleIndexedPropertyStorage::remove(u32 index)
{
if (index < m_array_size)
m_packed_elements[index] = {};
}
void SimpleIndexedPropertyStorage::insert(u32 index, Value value, PropertyAttributes attributes)
{
ASSERT(attributes == default_attributes);
ASSERT(index < SPARSE_ARRAY_THRESHOLD);
m_array_size++;
ASSERT(m_array_size <= SPARSE_ARRAY_THRESHOLD);
m_packed_elements.insert(index, value);
}
ValueAndAttributes SimpleIndexedPropertyStorage::take_first()
{
m_array_size--;
return { m_packed_elements.take_first(), default_attributes };
}
ValueAndAttributes SimpleIndexedPropertyStorage::take_last()
{
m_array_size--;
auto last_element = m_packed_elements[m_array_size];
m_packed_elements[m_array_size] = {};
return { last_element, default_attributes };
}
void SimpleIndexedPropertyStorage::set_array_like_size(size_t new_size)
{
ASSERT(new_size <= SPARSE_ARRAY_THRESHOLD);
m_array_size = new_size;
m_packed_elements.resize(new_size);
}
GenericIndexedPropertyStorage::GenericIndexedPropertyStorage(SimpleIndexedPropertyStorage&& storage)
{
m_array_size = storage.array_like_size();
for (auto& element : move(storage.m_packed_elements))
m_packed_elements.append({ element, default_attributes });
}
bool GenericIndexedPropertyStorage::has_index(u32 index) const
{
if (index < SPARSE_ARRAY_THRESHOLD)
return index < m_packed_elements.size() && !m_packed_elements[index].value.is_empty();
return m_sparse_elements.contains(index);
}
Optional<ValueAndAttributes> GenericIndexedPropertyStorage::get(u32 index) const
{
if (index >= m_array_size)
return {};
if (index < SPARSE_ARRAY_THRESHOLD) {
if (index >= m_packed_elements.size())
return {};
return m_packed_elements[index];
}
return m_sparse_elements.get(index);
}
void GenericIndexedPropertyStorage::put(u32 index, Value value, PropertyAttributes attributes)
{
if (index >= m_array_size)
m_array_size = index + 1;
if (index < SPARSE_ARRAY_THRESHOLD) {
if (index >= m_packed_elements.size())
m_packed_elements.resize(index + MIN_PACKED_RESIZE_AMOUNT >= SPARSE_ARRAY_THRESHOLD ? SPARSE_ARRAY_THRESHOLD : index + MIN_PACKED_RESIZE_AMOUNT);
m_packed_elements[index] = { value, attributes };
} else {
m_sparse_elements.set(index, { value, attributes });
}
}
void GenericIndexedPropertyStorage::remove(u32 index)
{
if (index >= m_array_size)
return;
if (index + 1 == m_array_size) {
take_last();
return;
}
if (index < SPARSE_ARRAY_THRESHOLD) {
if (index < m_packed_elements.size())
m_packed_elements[index] = {};
} else {
m_sparse_elements.remove(index);
}
}
void GenericIndexedPropertyStorage::insert(u32 index, Value value, PropertyAttributes attributes)
{
if (index >= m_array_size) {
put(index, value, attributes);
return;
}
m_array_size++;
if (!m_sparse_elements.is_empty()) {
HashMap<u32, ValueAndAttributes> new_sparse_elements;
for (auto& entry : m_sparse_elements)
new_sparse_elements.set(entry.key >= index ? entry.key + 1 : entry.key, entry.value);
m_sparse_elements = move(new_sparse_elements);
}
if (index < SPARSE_ARRAY_THRESHOLD) {
m_packed_elements.insert(index, { value, attributes });
} else {
m_sparse_elements.set(index, { value, attributes });
}
}
ValueAndAttributes GenericIndexedPropertyStorage::take_first()
{
ASSERT(m_array_size > 0);
m_array_size--;
if (!m_sparse_elements.is_empty()) {
HashMap<u32, ValueAndAttributes> new_sparse_elements;
for (auto& entry : m_sparse_elements)
new_sparse_elements.set(entry.key - 1, entry.value);
m_sparse_elements = move(new_sparse_elements);
}
return m_packed_elements.take_first();
}
ValueAndAttributes GenericIndexedPropertyStorage::take_last()
{
ASSERT(m_array_size > 0);
m_array_size--;
if (m_array_size <= SPARSE_ARRAY_THRESHOLD) {
auto last_element = m_packed_elements[m_array_size];
m_packed_elements[m_array_size] = {};
return last_element;
} else {
auto result = m_sparse_elements.get(m_array_size);
m_sparse_elements.remove(m_array_size);
ASSERT(result.has_value());
return result.value();
}
}
void GenericIndexedPropertyStorage::set_array_like_size(size_t new_size)
{
m_array_size = new_size;
if (new_size < SPARSE_ARRAY_THRESHOLD) {
m_packed_elements.resize(new_size);
m_sparse_elements.clear();
} else {
m_packed_elements.resize(SPARSE_ARRAY_THRESHOLD);
HashMap<u32, ValueAndAttributes> new_sparse_elements;
for (auto& entry : m_sparse_elements) {
if (entry.key < new_size)
new_sparse_elements.set(entry.key, entry.value);
}
m_sparse_elements = move(new_sparse_elements);
}
}
IndexedPropertyIterator::IndexedPropertyIterator(const IndexedProperties& indexed_properties, u32 staring_index, bool skip_empty)
: m_indexed_properties(indexed_properties)
, m_index(staring_index)
, m_skip_empty(skip_empty)
{
if (m_skip_empty)
skip_empty_indices();
}
IndexedPropertyIterator& IndexedPropertyIterator::operator++()
{
m_index++;
if (m_skip_empty)
skip_empty_indices();
return *this;
}
IndexedPropertyIterator& IndexedPropertyIterator::operator*()
{
return *this;
}
bool IndexedPropertyIterator::operator!=(const IndexedPropertyIterator& other) const
{
return m_index != other.m_index;
}
ValueAndAttributes IndexedPropertyIterator::value_and_attributes(Object* this_object, bool evaluate_accessors)
{
if (m_index < m_indexed_properties.array_like_size())
return m_indexed_properties.get(this_object, m_index, evaluate_accessors).value_or({});
return {};
}
void IndexedPropertyIterator::skip_empty_indices()
{
auto indices = m_indexed_properties.indices();
for (auto i : indices) {
if (i < m_index)
continue;
m_index = i;
return;
}
m_index = m_indexed_properties.array_like_size();
}
Optional<ValueAndAttributes> IndexedProperties::get(Object* this_object, u32 index, bool evaluate_accessors) const
{
auto result = m_storage->get(index);
if (!evaluate_accessors)
return result;
if (!result.has_value())
return {};
auto& value = result.value();
if (value.value.is_accessor()) {
ASSERT(this_object);
auto& accessor = value.value.as_accessor();
return ValueAndAttributes { accessor.call_getter(this_object), value.attributes };
}
return result;
}
void IndexedProperties::put(Object* this_object, u32 index, Value value, PropertyAttributes attributes, bool evaluate_accessors)
{
if (m_storage->is_simple_storage() && (index >= SPARSE_ARRAY_THRESHOLD || attributes != default_attributes))
switch_to_generic_storage();
if (m_storage->is_simple_storage() || !evaluate_accessors) {
m_storage->put(index, value, attributes);
return;
}
auto value_here = m_storage->get(index);
if (value_here.has_value() && value_here.value().value.is_accessor()) {
ASSERT(this_object);
value_here.value().value.as_accessor().call_setter(this_object, value);
} else {
m_storage->put(index, value, attributes);
}
}
bool IndexedProperties::remove(u32 index)
{
auto result = m_storage->get(index);
if (!result.has_value())
return true;
if (!result.value().attributes.is_configurable())
return false;
m_storage->remove(index);
return true;
}
void IndexedProperties::insert(u32 index, Value value, PropertyAttributes attributes)
{
if (m_storage->is_simple_storage() && (index >= SPARSE_ARRAY_THRESHOLD || attributes != default_attributes || array_like_size() == SPARSE_ARRAY_THRESHOLD))
switch_to_generic_storage();
m_storage->insert(index, move(value), attributes);
}
ValueAndAttributes IndexedProperties::take_first(Object* this_object)
{
auto first = m_storage->take_first();
if (first.value.is_accessor())
return { first.value.as_accessor().call_getter(this_object), first.attributes };
return first;
}
ValueAndAttributes IndexedProperties::take_last(Object* this_object)
{
auto last = m_storage->take_last();
if (last.value.is_accessor())
return { last.value.as_accessor().call_getter(this_object), last.attributes };
return last;
}
void IndexedProperties::append_all(Object* this_object, const IndexedProperties& properties, bool evaluate_accessors)
{
if (m_storage->is_simple_storage() && !properties.m_storage->is_simple_storage())
switch_to_generic_storage();
for (auto it = properties.begin(false); it != properties.end(); ++it) {
const auto& element = it.value_and_attributes(this_object, evaluate_accessors);
if (this_object && this_object->vm().exception())
return;
m_storage->put(m_storage->array_like_size(), element.value, element.attributes);
}
}
void IndexedProperties::set_array_like_size(size_t new_size)
{
if (m_storage->is_simple_storage() && new_size > SPARSE_ARRAY_THRESHOLD)
switch_to_generic_storage();
m_storage->set_array_like_size(new_size);
}
Vector<u32> IndexedProperties::indices() const
{
Vector<u32> indices;
if (m_storage->is_simple_storage()) {
const auto& storage = static_cast<const SimpleIndexedPropertyStorage&>(*m_storage);
const auto& elements = storage.elements();
indices.ensure_capacity(storage.array_like_size());
for (size_t i = 0; i < elements.size(); ++i) {
if (!elements.at(i).is_empty())
indices.unchecked_append(i);
}
} else {
const auto& storage = static_cast<const GenericIndexedPropertyStorage&>(*m_storage);
const auto packed_elements = storage.packed_elements();
indices.ensure_capacity(storage.array_like_size());
for (size_t i = 0; i < packed_elements.size(); ++i) {
if (!packed_elements.at(i).value.is_empty())
indices.unchecked_append(i);
}
auto sparse_elements_keys = storage.sparse_elements().keys();
quick_sort(sparse_elements_keys);
indices.append(move(sparse_elements_keys));
}
return indices;
}
void IndexedProperties::switch_to_generic_storage()
{
auto& storage = static_cast<SimpleIndexedPropertyStorage&>(*m_storage);
m_storage = make<GenericIndexedPropertyStorage>(move(storage));
}
}

View file

@ -0,0 +1,190 @@
/*
* 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/NonnullOwnPtr.h>
#include <LibJS/Runtime/Shape.h>
#include <LibJS/Runtime/Value.h>
namespace JS {
const u32 SPARSE_ARRAY_THRESHOLD = 200;
const u32 MIN_PACKED_RESIZE_AMOUNT = 20;
struct ValueAndAttributes {
Value value;
PropertyAttributes attributes { default_attributes };
};
class IndexedProperties;
class IndexedPropertyIterator;
class GenericIndexedPropertyStorage;
class IndexedPropertyStorage {
public:
virtual ~IndexedPropertyStorage() {};
virtual bool has_index(u32 index) const = 0;
virtual Optional<ValueAndAttributes> get(u32 index) const = 0;
virtual void put(u32 index, Value value, PropertyAttributes attributes = default_attributes) = 0;
virtual void remove(u32 index) = 0;
virtual void insert(u32 index, Value value, PropertyAttributes attributes = default_attributes) = 0;
virtual ValueAndAttributes take_first() = 0;
virtual ValueAndAttributes take_last() = 0;
virtual size_t size() const = 0;
virtual size_t array_like_size() const = 0;
virtual void set_array_like_size(size_t new_size) = 0;
virtual bool is_simple_storage() const { return false; }
};
class SimpleIndexedPropertyStorage final : public IndexedPropertyStorage {
public:
SimpleIndexedPropertyStorage() = default;
explicit SimpleIndexedPropertyStorage(Vector<Value>&& initial_values);
virtual bool has_index(u32 index) const override;
virtual Optional<ValueAndAttributes> get(u32 index) const override;
virtual void put(u32 index, Value value, PropertyAttributes attributes = default_attributes) override;
virtual void remove(u32 index) override;
virtual void insert(u32 index, Value value, PropertyAttributes attributes = default_attributes) override;
virtual ValueAndAttributes take_first() override;
virtual ValueAndAttributes take_last() override;
virtual size_t size() const override { return m_packed_elements.size(); }
virtual size_t array_like_size() const override { return m_array_size; }
virtual void set_array_like_size(size_t new_size) override;
virtual bool is_simple_storage() const override { return true; }
const Vector<Value>& elements() const { return m_packed_elements; }
private:
friend GenericIndexedPropertyStorage;
size_t m_array_size { 0 };
Vector<Value> m_packed_elements;
};
class GenericIndexedPropertyStorage final : public IndexedPropertyStorage {
public:
explicit GenericIndexedPropertyStorage(SimpleIndexedPropertyStorage&&);
virtual bool has_index(u32 index) const override;
virtual Optional<ValueAndAttributes> get(u32 index) const override;
virtual void put(u32 index, Value value, PropertyAttributes attributes = default_attributes) override;
virtual void remove(u32 index) override;
virtual void insert(u32 index, Value value, PropertyAttributes attributes = default_attributes) override;
virtual ValueAndAttributes take_first() override;
virtual ValueAndAttributes take_last() override;
virtual size_t size() const override { return m_packed_elements.size() + m_sparse_elements.size(); }
virtual size_t array_like_size() const override { return m_array_size; }
virtual void set_array_like_size(size_t new_size) override;
const Vector<ValueAndAttributes>& packed_elements() const { return m_packed_elements; }
const HashMap<u32, ValueAndAttributes>& sparse_elements() const { return m_sparse_elements; }
private:
size_t m_array_size { 0 };
Vector<ValueAndAttributes> m_packed_elements;
HashMap<u32, ValueAndAttributes> m_sparse_elements;
};
class IndexedPropertyIterator {
public:
IndexedPropertyIterator(const IndexedProperties&, u32 starting_index, bool skip_empty);
IndexedPropertyIterator& operator++();
IndexedPropertyIterator& operator*();
bool operator!=(const IndexedPropertyIterator&) const;
u32 index() const { return m_index; };
ValueAndAttributes value_and_attributes(Object* this_object, bool evaluate_accessors = true);
private:
void skip_empty_indices();
const IndexedProperties& m_indexed_properties;
u32 m_index;
bool m_skip_empty;
};
class IndexedProperties {
public:
IndexedProperties() = default;
IndexedProperties(Vector<Value>&& values)
: m_storage(make<SimpleIndexedPropertyStorage>(move(values)))
{
}
bool has_index(u32 index) const { return m_storage->has_index(index); }
Optional<ValueAndAttributes> get(Object* this_object, u32 index, bool evaluate_accessors = true) const;
void put(Object* this_object, u32 index, Value value, PropertyAttributes attributes = default_attributes, bool evaluate_accessors = true);
bool remove(u32 index);
void insert(u32 index, Value value, PropertyAttributes attributes = default_attributes);
ValueAndAttributes take_first(Object* this_object);
ValueAndAttributes take_last(Object* this_object);
void append(Value value, PropertyAttributes attributes = default_attributes) { put(nullptr, array_like_size(), value, attributes, false); }
void append_all(Object* this_object, const IndexedProperties& properties, bool evaluate_accessors = true);
IndexedPropertyIterator begin(bool skip_empty = true) const { return IndexedPropertyIterator(*this, 0, skip_empty); };
IndexedPropertyIterator end() const { return IndexedPropertyIterator(*this, array_like_size(), false); };
bool is_empty() const { return array_like_size() == 0; }
size_t array_like_size() const { return m_storage->array_like_size(); }
void set_array_like_size(size_t);
Vector<u32> indices() const;
template<typename Callback>
void for_each_value(Callback callback)
{
if (m_storage->is_simple_storage()) {
for (auto& value : static_cast<SimpleIndexedPropertyStorage&>(*m_storage).elements())
callback(value);
} else {
for (auto& element : static_cast<const GenericIndexedPropertyStorage&>(*m_storage).packed_elements())
callback(element.value);
for (auto& element : static_cast<const GenericIndexedPropertyStorage&>(*m_storage).sparse_elements())
callback(element.value.value);
}
}
private:
void switch_to_generic_storage();
NonnullOwnPtr<IndexedPropertyStorage> m_storage { make<SimpleIndexedPropertyStorage>() };
};
}

View file

@ -0,0 +1,135 @@
/*
* 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/Runtime/Error.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/IteratorOperations.h>
namespace JS {
Object* get_iterator(GlobalObject& global_object, Value value, String hint, Value method)
{
auto& vm = global_object.vm();
ASSERT(hint == "sync" || hint == "async");
if (method.is_empty()) {
if (hint == "async")
TODO();
auto object = value.to_object(global_object);
if (!object)
return {};
method = object->get(global_object.vm().well_known_symbol_iterator());
if (vm.exception())
return {};
}
if (!method.is_function()) {
vm.throw_exception<TypeError>(global_object, ErrorType::NotIterable, value.to_string_without_side_effects());
return nullptr;
}
auto iterator = vm.call(method.as_function(), value);
if (vm.exception())
return {};
if (!iterator.is_object()) {
vm.throw_exception<TypeError>(global_object, ErrorType::NotIterable, value.to_string_without_side_effects());
return nullptr;
}
return &iterator.as_object();
}
Object* iterator_next(Object& iterator, Value value)
{
auto& vm = iterator.vm();
auto& global_object = iterator.global_object();
auto next_method = iterator.get(vm.names.next);
if (vm.exception())
return {};
if (!next_method.is_function()) {
vm.throw_exception<TypeError>(global_object, ErrorType::IterableNextNotAFunction);
return nullptr;
}
Value result;
if (value.is_empty())
result = vm.call(next_method.as_function(), &iterator);
else
result = vm.call(next_method.as_function(), &iterator, value);
if (vm.exception())
return {};
if (!result.is_object()) {
vm.throw_exception<TypeError>(global_object, ErrorType::IterableNextBadReturn);
return nullptr;
}
return &result.as_object();
}
void iterator_close([[maybe_unused]] Object& iterator)
{
TODO();
}
Value create_iterator_result_object(GlobalObject& global_object, Value value, bool done)
{
auto& vm = global_object.vm();
auto* object = Object::create_empty(global_object);
object->define_property(vm.names.value, value);
object->define_property(vm.names.done, Value(done));
return object;
}
void get_iterator_values(GlobalObject& global_object, Value value, AK::Function<IterationDecision(Value)> callback)
{
auto& vm = global_object.vm();
auto iterator = get_iterator(global_object, value);
if (!iterator)
return;
while (true) {
auto next_object = iterator_next(*iterator);
if (!next_object)
return;
auto done_property = next_object->get(vm.names.done);
if (vm.exception())
return;
if (!done_property.is_empty() && done_property.to_boolean())
return;
auto next_value = next_object->get(vm.names.value);
if (vm.exception())
return;
auto result = callback(next_value);
if (result == IterationDecision::Break)
return;
ASSERT(result == IterationDecision::Continue);
}
}
}

View file

@ -0,0 +1,46 @@
/*
* 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/Function.h>
#include <LibJS/Runtime/Object.h>
namespace JS {
// Common iterator operations defined in ECMA262 7.4
// https://tc39.es/ecma262/#sec-operations-on-iterator-objects
Object* get_iterator(GlobalObject&, Value value, String hint = "sync", Value method = {});
bool is_iterator_complete(Object& iterator_result);
Value create_iterator_result_object(GlobalObject&, Value value, bool done);
Object* iterator_next(Object& iterator, Value value = {});
void iterator_close(Object& iterator);
void get_iterator_values(GlobalObject&, Value value, AK::Function<IterationDecision(Value)> callback);
}

View file

@ -0,0 +1,56 @@
/*
* 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 <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/IteratorPrototype.h>
namespace JS {
IteratorPrototype::IteratorPrototype(GlobalObject& global_object)
: Object(*global_object.object_prototype())
{
}
void IteratorPrototype::initialize(GlobalObject& global_object)
{
Object::initialize(global_object);
define_native_function(global_object.vm().well_known_symbol_iterator(), symbol_iterator, 0, Attribute::Writable | Attribute::Enumerable);
}
IteratorPrototype::~IteratorPrototype()
{
}
JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::symbol_iterator)
{
auto* this_object = vm.this_value(global_object).to_object(global_object);
if (!this_object)
return {};
return this_object;
}
}

View file

@ -0,0 +1,45 @@
/*
* 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 IteratorPrototype : public Object {
JS_OBJECT(IteratorPrototype, Object)
public:
IteratorPrototype(GlobalObject&);
virtual void initialize(GlobalObject&) override;
virtual ~IteratorPrototype() override;
private:
JS_DECLARE_NATIVE_FUNCTION(symbol_iterator);
};
}

View file

@ -0,0 +1,507 @@
/*
* 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/JsonArray.h>
#include <AK/JsonObject.h>
#include <AK/JsonParser.h>
#include <AK/StringBuilder.h>
#include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/BigIntObject.h>
#include <LibJS/Runtime/BooleanObject.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/JSONObject.h>
#include <LibJS/Runtime/NumberObject.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/StringObject.h>
namespace JS {
JSONObject::JSONObject(GlobalObject& global_object)
: Object(*global_object.object_prototype())
{
}
void JSONObject::initialize(GlobalObject& global_object)
{
auto& vm = this->vm();
Object::initialize(global_object);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(vm.names.stringify, stringify, 3, attr);
define_native_function(vm.names.parse, parse, 2, attr);
define_property(vm.well_known_symbol_to_string_tag(), js_string(global_object.heap(), "JSON"), Attribute::Configurable);
}
JSONObject::~JSONObject()
{
}
String JSONObject::stringify_impl(GlobalObject& global_object, Value value, Value replacer, Value space)
{
auto& vm = global_object.vm();
StringifyState state;
if (replacer.is_object()) {
if (replacer.as_object().is_function()) {
state.replacer_function = &replacer.as_function();
} else if (replacer.is_array()) {
auto& replacer_object = replacer.as_object();
auto replacer_length = length_of_array_like(global_object, replacer_object);
if (vm.exception())
return {};
Vector<String> list;
for (size_t i = 0; i < replacer_length; ++i) {
auto replacer_value = replacer_object.get(i);
if (vm.exception())
return {};
String item;
if (replacer_value.is_string() || replacer_value.is_number()) {
item = replacer_value.to_string(global_object);
if (vm.exception())
return {};
} else if (replacer_value.is_object()) {
auto& value_object = replacer_value.as_object();
if (is<StringObject>(value_object) || is<NumberObject>(value_object)) {
item = value_object.value_of().to_string(global_object);
if (vm.exception())
return {};
}
}
if (!item.is_null() && !list.contains_slow(item)) {
list.append(item);
}
}
state.property_list = list;
}
}
if (space.is_object()) {
auto& space_obj = space.as_object();
if (is<StringObject>(space_obj) || is<NumberObject>(space_obj))
space = space_obj.value_of();
}
if (space.is_number()) {
StringBuilder gap_builder;
auto gap_size = min(10, space.as_i32());
for (auto i = 0; i < gap_size; ++i)
gap_builder.append(' ');
state.gap = gap_builder.to_string();
} else if (space.is_string()) {
auto string = space.as_string().string();
if (string.length() <= 10) {
state.gap = string;
} else {
state.gap = string.substring(0, 10);
}
} else {
state.gap = String::empty();
}
auto* wrapper = Object::create_empty(global_object);
wrapper->define_property(String::empty(), value);
if (vm.exception())
return {};
auto result = serialize_json_property(global_object, state, String::empty(), wrapper);
if (vm.exception())
return {};
if (result.is_null())
return {};
return result;
}
JS_DEFINE_NATIVE_FUNCTION(JSONObject::stringify)
{
if (!vm.argument_count())
return js_undefined();
auto value = vm.argument(0);
auto replacer = vm.argument(1);
auto space = vm.argument(2);
auto string = stringify_impl(global_object, value, replacer, space);
if (string.is_null())
return js_undefined();
return js_string(vm, string);
}
String JSONObject::serialize_json_property(GlobalObject& global_object, StringifyState& state, const PropertyName& key, Object* holder)
{
auto& vm = global_object.vm();
auto value = holder->get(key);
if (vm.exception())
return {};
if (value.is_object()) {
auto to_json = value.as_object().get(vm.names.toJSON);
if (vm.exception())
return {};
if (to_json.is_function()) {
value = vm.call(to_json.as_function(), value, js_string(vm, key.to_string()));
if (vm.exception())
return {};
}
}
if (state.replacer_function) {
value = vm.call(*state.replacer_function, holder, js_string(vm, key.to_string()), value);
if (vm.exception())
return {};
}
if (value.is_object()) {
auto& value_object = value.as_object();
if (is<NumberObject>(value_object) || is<BooleanObject>(value_object) || is<StringObject>(value_object) || is<BigIntObject>(value_object))
value = value_object.value_of();
}
if (value.is_null())
return "null";
if (value.is_boolean())
return value.as_bool() ? "true" : "false";
if (value.is_string())
return quote_json_string(value.as_string().string());
if (value.is_number()) {
if (value.is_finite_number())
return value.to_string(global_object);
return "null";
}
if (value.is_object() && !value.is_function()) {
if (value.is_array())
return serialize_json_array(global_object, state, static_cast<Array&>(value.as_object()));
return serialize_json_object(global_object, state, value.as_object());
}
if (value.is_bigint())
vm.throw_exception<TypeError>(global_object, ErrorType::JsonBigInt);
return {};
}
String JSONObject::serialize_json_object(GlobalObject& global_object, StringifyState& state, Object& object)
{
auto& vm = global_object.vm();
if (state.seen_objects.contains(&object)) {
vm.throw_exception<TypeError>(global_object, ErrorType::JsonCircular);
return {};
}
state.seen_objects.set(&object);
String previous_indent = state.indent;
state.indent = String::formatted("{}{}", state.indent, state.gap);
Vector<String> property_strings;
auto process_property = [&](const PropertyName& key) {
auto serialized_property_string = serialize_json_property(global_object, state, key, &object);
if (vm.exception())
return;
if (!serialized_property_string.is_null()) {
property_strings.append(String::formatted(
"{}:{}{}",
quote_json_string(key.to_string()),
state.gap.is_empty() ? "" : " ",
serialized_property_string));
}
};
if (state.property_list.has_value()) {
auto property_list = state.property_list.value();
for (auto& property : property_list) {
process_property(property);
if (vm.exception())
return {};
}
} else {
for (auto& entry : object.indexed_properties()) {
auto value_and_attributes = entry.value_and_attributes(&object);
if (!value_and_attributes.attributes.is_enumerable())
continue;
process_property(entry.index());
if (vm.exception())
return {};
}
for (auto& [key, metadata] : object.shape().property_table_ordered()) {
if (!metadata.attributes.is_enumerable())
continue;
process_property(key);
if (vm.exception())
return {};
}
}
StringBuilder builder;
if (property_strings.is_empty()) {
builder.append("{}");
} else {
bool first = true;
builder.append('{');
if (state.gap.is_empty()) {
for (auto& property_string : property_strings) {
if (!first)
builder.append(',');
first = false;
builder.append(property_string);
}
} else {
builder.append('\n');
builder.append(state.indent);
auto separator = String::formatted(",\n{}", state.indent);
for (auto& property_string : property_strings) {
if (!first)
builder.append(separator);
first = false;
builder.append(property_string);
}
builder.append('\n');
builder.append(previous_indent);
}
builder.append('}');
}
state.seen_objects.remove(&object);
state.indent = previous_indent;
return builder.to_string();
}
String JSONObject::serialize_json_array(GlobalObject& global_object, StringifyState& state, Object& object)
{
auto& vm = global_object.vm();
if (state.seen_objects.contains(&object)) {
vm.throw_exception<TypeError>(global_object, ErrorType::JsonCircular);
return {};
}
state.seen_objects.set(&object);
String previous_indent = state.indent;
state.indent = String::formatted("{}{}", state.indent, state.gap);
Vector<String> property_strings;
auto length = length_of_array_like(global_object, object);
if (vm.exception())
return {};
for (size_t i = 0; i < length; ++i) {
if (vm.exception())
return {};
auto serialized_property_string = serialize_json_property(global_object, state, i, &object);
if (vm.exception())
return {};
if (serialized_property_string.is_null()) {
property_strings.append("null");
} else {
property_strings.append(serialized_property_string);
}
}
StringBuilder builder;
if (property_strings.is_empty()) {
builder.append("[]");
} else {
if (state.gap.is_empty()) {
builder.append('[');
bool first = true;
for (auto& property_string : property_strings) {
if (!first)
builder.append(',');
first = false;
builder.append(property_string);
}
builder.append(']');
} else {
builder.append("[\n");
builder.append(state.indent);
auto separator = String::formatted(",\n{}", state.indent);
bool first = true;
for (auto& property_string : property_strings) {
if (!first)
builder.append(separator);
first = false;
builder.append(property_string);
}
builder.append('\n');
builder.append(previous_indent);
builder.append(']');
}
}
state.seen_objects.remove(&object);
state.indent = previous_indent;
return builder.to_string();
}
String JSONObject::quote_json_string(String string)
{
// FIXME: Handle UTF16
StringBuilder builder;
builder.append('"');
for (auto& ch : string) {
switch (ch) {
case '\b':
builder.append("\\b");
break;
case '\t':
builder.append("\\t");
break;
case '\n':
builder.append("\\n");
break;
case '\f':
builder.append("\\f");
break;
case '\r':
builder.append("\\r");
break;
case '"':
builder.append("\\\"");
break;
case '\\':
builder.append("\\\\");
break;
default:
if (ch < 0x20) {
builder.append("\\u%#08x", ch);
} else {
builder.append(ch);
}
}
}
builder.append('"');
return builder.to_string();
}
JS_DEFINE_NATIVE_FUNCTION(JSONObject::parse)
{
if (!vm.argument_count())
return js_undefined();
auto string = vm.argument(0).to_string(global_object);
if (vm.exception())
return {};
auto reviver = vm.argument(1);
auto json = JsonValue::from_string(string);
if (!json.has_value()) {
vm.throw_exception<SyntaxError>(global_object, ErrorType::JsonMalformed);
return {};
}
Value result = parse_json_value(global_object, json.value());
if (reviver.is_function()) {
auto* holder_object = Object::create_empty(global_object);
holder_object->define_property(String::empty(), result);
if (vm.exception())
return {};
return internalize_json_property(global_object, holder_object, String::empty(), reviver.as_function());
}
return result;
}
Value JSONObject::parse_json_value(GlobalObject& global_object, const JsonValue& value)
{
if (value.is_object())
return Value(parse_json_object(global_object, value.as_object()));
if (value.is_array())
return Value(parse_json_array(global_object, value.as_array()));
if (value.is_null())
return js_null();
#if !defined(KERNEL)
if (value.is_double())
return Value(value.as_double());
#endif
if (value.is_number())
return Value(value.to_i32(0));
if (value.is_string())
return js_string(global_object.heap(), value.to_string());
if (value.is_bool())
return Value(static_cast<bool>(value.as_bool()));
ASSERT_NOT_REACHED();
}
Object* JSONObject::parse_json_object(GlobalObject& global_object, const JsonObject& json_object)
{
auto* object = Object::create_empty(global_object);
json_object.for_each_member([&](auto& key, auto& value) {
object->define_property(key, parse_json_value(global_object, value));
});
return object;
}
Array* JSONObject::parse_json_array(GlobalObject& global_object, const JsonArray& json_array)
{
auto* array = Array::create(global_object);
size_t index = 0;
json_array.for_each([&](auto& value) {
array->define_property(index++, parse_json_value(global_object, value));
});
return array;
}
Value JSONObject::internalize_json_property(GlobalObject& global_object, Object* holder, const PropertyName& name, Function& reviver)
{
auto& vm = global_object.vm();
auto value = holder->get(name);
if (vm.exception())
return {};
if (value.is_object()) {
auto& value_object = value.as_object();
auto process_property = [&](const PropertyName& key) {
auto element = internalize_json_property(global_object, &value_object, key, reviver);
if (vm.exception())
return;
if (element.is_undefined()) {
value_object.delete_property(key);
} else {
value_object.define_property(key, element, default_attributes, false);
}
};
if (value_object.is_array()) {
auto length = length_of_array_like(global_object, value_object);
for (size_t i = 0; i < length; ++i) {
process_property(i);
if (vm.exception())
return {};
}
} else {
for (auto& entry : value_object.indexed_properties()) {
auto value_and_attributes = entry.value_and_attributes(&value_object);
if (!value_and_attributes.attributes.is_enumerable())
continue;
process_property(entry.index());
if (vm.exception())
return {};
}
for (auto& [key, metadata] : value_object.shape().property_table_ordered()) {
if (!metadata.attributes.is_enumerable())
continue;
process_property(key);
if (vm.exception())
return {};
}
}
}
return vm.call(reviver, Value(holder), js_string(vm, name.to_string()), value);
}
}

View file

@ -0,0 +1,70 @@
/*
* 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 JSONObject final : public Object {
JS_OBJECT(JSONObject, Object);
public:
explicit JSONObject(GlobalObject&);
virtual void initialize(GlobalObject&) override;
virtual ~JSONObject() override;
// The base implementation of stringify is exposed because it is used by
// test-js to communicate between the JS tests and the C++ test runner.
static String stringify_impl(GlobalObject&, Value value, Value replacer, Value space);
private:
struct StringifyState {
Function* replacer_function { nullptr };
HashTable<Object*> seen_objects;
String indent { String::empty() };
String gap;
Optional<Vector<String>> property_list;
};
// Stringify helpers
static String serialize_json_property(GlobalObject&, StringifyState&, const PropertyName& key, Object* holder);
static String serialize_json_object(GlobalObject&, StringifyState&, Object&);
static String serialize_json_array(GlobalObject&, StringifyState&, Object&);
static String quote_json_string(String);
// Parse helpers
static Object* parse_json_object(GlobalObject&, const JsonObject&);
static Array* parse_json_array(GlobalObject&, const JsonArray&);
static Value parse_json_value(GlobalObject&, const JsonValue&);
static Value internalize_json_property(GlobalObject&, Object* holder, const PropertyName& name, Function& reviver);
JS_DECLARE_NATIVE_FUNCTION(stringify);
JS_DECLARE_NATIVE_FUNCTION(parse);
};
}

View file

@ -0,0 +1,134 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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/Function.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/LexicalEnvironment.h>
#include <LibJS/Runtime/Value.h>
namespace JS {
LexicalEnvironment::LexicalEnvironment()
: ScopeObject(nullptr)
{
}
LexicalEnvironment::LexicalEnvironment(EnvironmentRecordType environment_record_type)
: ScopeObject(nullptr)
, m_environment_record_type(environment_record_type)
{
}
LexicalEnvironment::LexicalEnvironment(HashMap<FlyString, Variable> variables, ScopeObject* parent_scope)
: ScopeObject(parent_scope)
, m_variables(move(variables))
{
}
LexicalEnvironment::LexicalEnvironment(HashMap<FlyString, Variable> variables, ScopeObject* parent_scope, EnvironmentRecordType environment_record_type)
: ScopeObject(parent_scope)
, m_environment_record_type(environment_record_type)
, m_variables(move(variables))
{
}
LexicalEnvironment::~LexicalEnvironment()
{
}
void LexicalEnvironment::visit_edges(Visitor& visitor)
{
Cell::visit_edges(visitor);
visitor.visit(m_this_value);
visitor.visit(m_home_object);
visitor.visit(m_new_target);
visitor.visit(m_current_function);
for (auto& it : m_variables)
visitor.visit(it.value.value);
}
Optional<Variable> LexicalEnvironment::get_from_scope(const FlyString& name) const
{
return m_variables.get(name);
}
void LexicalEnvironment::put_to_scope(const FlyString& name, Variable variable)
{
m_variables.set(name, variable);
}
bool LexicalEnvironment::has_super_binding() const
{
return m_environment_record_type == EnvironmentRecordType::Function && this_binding_status() != ThisBindingStatus::Lexical && m_home_object.is_object();
}
Value LexicalEnvironment::get_super_base()
{
ASSERT(has_super_binding());
if (m_home_object.is_object())
return m_home_object.as_object().prototype();
return {};
}
bool LexicalEnvironment::has_this_binding() const
{
// More like "is_capable_of_having_a_this_binding".
switch (m_environment_record_type) {
case EnvironmentRecordType::Declarative:
case EnvironmentRecordType::Object:
return false;
case EnvironmentRecordType::Function:
return this_binding_status() != ThisBindingStatus::Lexical;
case EnvironmentRecordType::Module:
return true;
}
ASSERT_NOT_REACHED();
}
Value LexicalEnvironment::get_this_binding(GlobalObject& global_object) const
{
ASSERT(has_this_binding());
if (this_binding_status() == ThisBindingStatus::Uninitialized) {
vm().throw_exception<ReferenceError>(global_object, ErrorType::ThisHasNotBeenInitialized);
return {};
}
return m_this_value;
}
void LexicalEnvironment::bind_this_value(GlobalObject& global_object, Value this_value)
{
ASSERT(has_this_binding());
if (m_this_binding_status == ThisBindingStatus::Initialized) {
vm().throw_exception<ReferenceError>(global_object, ErrorType::ThisIsAlreadyInitialized);
return;
}
m_this_value = this_value;
m_this_binding_status = ThisBindingStatus::Initialized;
}
}

View file

@ -0,0 +1,100 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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/FlyString.h>
#include <AK/HashMap.h>
#include <LibJS/Runtime/ScopeObject.h>
#include <LibJS/Runtime/Value.h>
namespace JS {
class LexicalEnvironment final : public ScopeObject {
JS_OBJECT(LexicalEnvironment, ScopeObject);
public:
enum class ThisBindingStatus {
Lexical,
Initialized,
Uninitialized,
};
enum class EnvironmentRecordType {
Declarative,
Function,
Object,
Module,
};
LexicalEnvironment();
LexicalEnvironment(EnvironmentRecordType);
LexicalEnvironment(HashMap<FlyString, Variable> variables, ScopeObject* parent_scope);
LexicalEnvironment(HashMap<FlyString, Variable> variables, ScopeObject* parent_scope, EnvironmentRecordType);
virtual ~LexicalEnvironment() override;
// ^ScopeObject
virtual Optional<Variable> get_from_scope(const FlyString&) const override;
virtual void put_to_scope(const FlyString&, Variable) override;
virtual bool has_this_binding() const override;
virtual Value get_this_binding(GlobalObject&) const override;
void clear();
const HashMap<FlyString, Variable>& variables() const { return m_variables; }
void set_home_object(Value object) { m_home_object = object; }
bool has_super_binding() const;
Value get_super_base();
ThisBindingStatus this_binding_status() const { return m_this_binding_status; }
void bind_this_value(GlobalObject&, Value this_value);
// Not a standard operation.
void replace_this_binding(Value this_value) { m_this_value = this_value; }
Value new_target() const { return m_new_target; };
void set_new_target(Value new_target) { m_new_target = new_target; }
Function* current_function() const { return m_current_function; }
void set_current_function(Function& function) { m_current_function = &function; }
EnvironmentRecordType type() const { return m_environment_record_type; }
private:
virtual void visit_edges(Visitor&) override;
EnvironmentRecordType m_environment_record_type : 8 { EnvironmentRecordType::Declarative };
ThisBindingStatus m_this_binding_status : 8 { ThisBindingStatus::Uninitialized };
HashMap<FlyString, Variable> m_variables;
Value m_home_object;
Value m_this_value;
Value m_new_target;
// Corresponds to [[FunctionObject]]
Function* m_current_function { nullptr };
};
}

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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/Runtime/MarkedValueList.h>
namespace JS {
MarkedValueList::MarkedValueList(Heap& heap)
: m_heap(heap)
{
m_heap.did_create_marked_value_list({}, *this);
}
MarkedValueList::MarkedValueList(MarkedValueList&& other)
: AK::Vector<Value, 32>(move(static_cast<Vector<Value, 32>&>(other)))
, m_heap(other.m_heap)
{
m_heap.did_create_marked_value_list({}, *this);
}
MarkedValueList::~MarkedValueList()
{
m_heap.did_destroy_marked_value_list({}, *this);
}
}

View file

@ -0,0 +1,58 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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/Noncopyable.h>
#include <AK/Vector.h>
#include <LibJS/Forward.h>
#include <LibJS/Runtime/Value.h>
namespace JS {
class MarkedValueList : public AK::Vector<Value, 32> {
AK_MAKE_NONCOPYABLE(MarkedValueList);
public:
explicit MarkedValueList(Heap&);
MarkedValueList(MarkedValueList&&);
~MarkedValueList();
MarkedValueList& operator=(MarkedValueList&&) = delete;
Vector<Value, 32>& values() { return *this; }
MarkedValueList copy() const
{
MarkedValueList copy { m_heap };
copy.append(*this);
return copy;
}
private:
Heap& m_heap;
};
}

View file

@ -0,0 +1,504 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* 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/FlyString.h>
#include <AK/Function.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/MathObject.h>
#include <math.h>
namespace JS {
MathObject::MathObject(GlobalObject& global_object)
: Object(*global_object.object_prototype())
{
}
void MathObject::initialize(GlobalObject& global_object)
{
auto& vm = this->vm();
Object::initialize(global_object);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(vm.names.abs, abs, 1, attr);
define_native_function(vm.names.random, random, 0, attr);
define_native_function(vm.names.sqrt, sqrt, 1, attr);
define_native_function(vm.names.floor, floor, 1, attr);
define_native_function(vm.names.ceil, ceil, 1, attr);
define_native_function(vm.names.round, round, 1, attr);
define_native_function(vm.names.max, max, 2, attr);
define_native_function(vm.names.min, min, 2, attr);
define_native_function(vm.names.trunc, trunc, 1, attr);
define_native_function(vm.names.sin, sin, 1, attr);
define_native_function(vm.names.cos, cos, 1, attr);
define_native_function(vm.names.tan, tan, 1, attr);
define_native_function(vm.names.pow, pow, 2, attr);
define_native_function(vm.names.exp, exp, 1, attr);
define_native_function(vm.names.expm1, expm1, 1, attr);
define_native_function(vm.names.sign, sign, 1, attr);
define_native_function(vm.names.clz32, clz32, 1, attr);
define_native_function(vm.names.acos, acos, 1, attr);
define_native_function(vm.names.acosh, acosh, 1, attr);
define_native_function(vm.names.asin, asin, 1, attr);
define_native_function(vm.names.asinh, asinh, 1, attr);
define_native_function(vm.names.atan, atan, 1, attr);
define_native_function(vm.names.atanh, atanh, 1, attr);
define_native_function(vm.names.log1p, log1p, 1, attr);
define_native_function(vm.names.cbrt, cbrt, 1, attr);
define_native_function(vm.names.atan2, atan2, 2, attr);
define_native_function(vm.names.fround, fround, 1, attr);
define_native_function(vm.names.hypot, hypot, 2, attr);
define_native_function(vm.names.log, log, 1, attr);
define_native_function(vm.names.log2, log2, 1, attr);
define_native_function(vm.names.log10, log10, 1, attr);
define_native_function(vm.names.sinh, sinh, 1, attr);
define_native_function(vm.names.cosh, cosh, 1, attr);
define_native_function(vm.names.tanh, tanh, 1, attr);
define_property(vm.names.E, Value(M_E), 0);
define_property(vm.names.LN2, Value(M_LN2), 0);
define_property(vm.names.LN10, Value(M_LN10), 0);
define_property(vm.names.LOG2E, Value(::log2(M_E)), 0);
define_property(vm.names.LOG10E, Value(::log10(M_E)), 0);
define_property(vm.names.PI, Value(M_PI), 0);
define_property(vm.names.SQRT1_2, Value(M_SQRT1_2), 0);
define_property(vm.names.SQRT2, Value(M_SQRT2), 0);
define_property(vm.well_known_symbol_to_string_tag(), js_string(vm.heap(), "Math"), Attribute::Configurable);
}
MathObject::~MathObject()
{
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::abs)
{
auto number = vm.argument(0).to_number(global_object);
if (vm.exception())
return {};
if (number.is_nan())
return js_nan();
return Value(number.as_double() >= 0 ? number.as_double() : -number.as_double());
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::random)
{
#ifdef __serenity__
double r = (double)arc4random() / (double)UINT32_MAX;
#else
double r = (double)rand() / (double)RAND_MAX;
#endif
return Value(r);
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::sqrt)
{
auto number = vm.argument(0).to_number(global_object);
if (vm.exception())
return {};
if (number.is_nan())
return js_nan();
return Value(::sqrt(number.as_double()));
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::floor)
{
auto number = vm.argument(0).to_number(global_object);
if (vm.exception())
return {};
if (number.is_nan())
return js_nan();
return Value(::floor(number.as_double()));
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::ceil)
{
auto number = vm.argument(0).to_number(global_object);
if (vm.exception())
return {};
if (number.is_nan())
return js_nan();
auto number_double = number.as_double();
if (number_double < 0 && number_double > -1)
return Value(-0.f);
return Value(::ceil(number.as_double()));
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::round)
{
auto number = vm.argument(0).to_number(global_object);
if (vm.exception())
return {};
if (number.is_nan())
return js_nan();
return Value(::round(number.as_double()));
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::max)
{
if (!vm.argument_count())
return js_negative_infinity();
auto max = vm.argument(0).to_number(global_object);
if (vm.exception())
return {};
for (size_t i = 1; i < vm.argument_count(); ++i) {
auto cur = vm.argument(i).to_number(global_object);
if (vm.exception())
return {};
max = Value(cur.as_double() > max.as_double() ? cur : max);
}
return max;
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::min)
{
if (!vm.argument_count())
return js_infinity();
auto min = vm.argument(0).to_number(global_object);
if (vm.exception())
return {};
for (size_t i = 1; i < vm.argument_count(); ++i) {
auto cur = vm.argument(i).to_number(global_object);
if (vm.exception())
return {};
min = Value(cur.as_double() < min.as_double() ? cur : min);
}
return min;
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::trunc)
{
auto number = vm.argument(0).to_number(global_object);
if (vm.exception())
return {};
if (number.is_nan())
return js_nan();
if (number.as_double() < 0)
return MathObject::ceil(vm, global_object);
return MathObject::floor(vm, global_object);
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::sin)
{
auto number = vm.argument(0).to_number(global_object);
if (vm.exception())
return {};
if (number.is_nan())
return js_nan();
return Value(::sin(number.as_double()));
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::cos)
{
auto number = vm.argument(0).to_number(global_object);
if (vm.exception())
return {};
if (number.is_nan())
return js_nan();
return Value(::cos(number.as_double()));
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::tan)
{
auto number = vm.argument(0).to_number(global_object);
if (vm.exception())
return {};
if (number.is_nan())
return js_nan();
return Value(::tan(number.as_double()));
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::pow)
{
return JS::exp(global_object, vm.argument(0), vm.argument(1));
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::exp)
{
auto number = vm.argument(0).to_number(global_object);
if (vm.exception())
return {};
if (number.is_nan())
return js_nan();
return Value(::exp(number.as_double()));
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::expm1)
{
auto number = vm.argument(0).to_number(global_object);
if (vm.exception())
return {};
if (number.is_nan())
return js_nan();
return Value(::expm1(number.as_double()));
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::sign)
{
auto number = vm.argument(0).to_number(global_object);
if (vm.exception())
return {};
if (number.is_positive_zero())
return Value(0);
if (number.is_negative_zero())
return Value(-0.0);
if (number.as_double() > 0)
return Value(1);
if (number.as_double() < 0)
return Value(-1);
return js_nan();
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::clz32)
{
auto number = vm.argument(0).to_number(global_object);
if (vm.exception())
return {};
if (!number.is_finite_number() || (unsigned)number.as_double() == 0)
return Value(32);
return Value(__builtin_clz((unsigned)number.as_double()));
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::acos)
{
auto number = vm.argument(0).to_number(global_object);
if (vm.exception())
return {};
if (number.is_nan() || number.as_double() > 1 || number.as_double() < -1)
return js_nan();
if (number.as_double() == 1)
return Value(0);
return Value(::acos(number.as_double()));
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::acosh)
{
auto number = vm.argument(0).to_number(global_object);
if (vm.exception())
return {};
if (number.as_double() < 1)
return js_nan();
return Value(::acosh(number.as_double()));
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::asin)
{
auto number = vm.argument(0).to_number(global_object);
if (vm.exception())
return {};
if (number.is_nan() || number.is_positive_zero() || number.is_negative_zero())
return number;
return Value(::asin(number.as_double()));
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::asinh)
{
auto number = vm.argument(0).to_number(global_object);
if (vm.exception())
return {};
return Value(::asinh(number.as_double()));
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::atan)
{
auto number = vm.argument(0).to_number(global_object);
if (vm.exception())
return {};
if (number.is_nan() || number.is_positive_zero() || number.is_negative_zero())
return number;
if (number.is_positive_infinity())
return Value(M_PI_2);
if (number.is_negative_infinity())
return Value(-M_PI_2);
return Value(::atan(number.as_double()));
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::atanh)
{
auto number = vm.argument(0).to_number(global_object);
if (vm.exception())
return {};
if (number.as_double() > 1 || number.as_double() < -1)
return js_nan();
return Value(::atanh(number.as_double()));
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::log1p)
{
auto number = vm.argument(0).to_number(global_object);
if (vm.exception())
return {};
if (number.as_double() < -1)
return js_nan();
return Value(::log1p(number.as_double()));
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::cbrt)
{
auto number = vm.argument(0).to_number(global_object);
if (vm.exception())
return {};
return Value(::cbrt(number.as_double()));
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::atan2)
{
auto y = vm.argument(0).to_number(global_object), x = vm.argument(1).to_number(global_object);
auto pi_4 = M_PI_2 / 2;
auto three_pi_4 = pi_4 + M_PI_2;
if (vm.exception())
return {};
if (x.is_positive_zero()) {
if (y.is_positive_zero() || y.is_negative_zero())
return y;
else
return (y.as_double() > 0) ? Value(M_PI_2) : Value(-M_PI_2);
}
if (x.is_negative_zero()) {
if (y.is_positive_zero())
return Value(M_PI);
else if (y.is_negative_zero())
return Value(-M_PI);
else
return (y.as_double() > 0) ? Value(M_PI_2) : Value(-M_PI_2);
}
if (x.is_positive_infinity()) {
if (y.is_infinity())
return (y.is_positive_infinity()) ? Value(pi_4) : Value(-pi_4);
else
return (y.as_double() > 0) ? Value(+0.0) : Value(-0.0);
}
if (x.is_negative_infinity()) {
if (y.is_infinity())
return (y.is_positive_infinity()) ? Value(three_pi_4) : Value(-three_pi_4);
else
return (y.as_double() > 0) ? Value(M_PI) : Value(-M_PI);
}
if (y.is_infinity())
return (y.is_positive_infinity()) ? Value(M_PI_2) : Value(-M_PI_2);
if (y.is_positive_zero())
return (x.as_double() > 0) ? Value(+0.0) : Value(M_PI);
if (y.is_negative_zero())
return (x.as_double() > 0) ? Value(-0.0) : Value(-M_PI);
return Value(::atan2(y.as_double(), x.as_double()));
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::fround)
{
auto number = vm.argument(0).to_number(global_object);
if (vm.exception())
return {};
if (number.is_nan())
return js_nan();
return Value((float)number.as_double());
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::hypot)
{
if (!vm.argument_count())
return Value(0);
auto hypot = vm.argument(0).to_number(global_object);
if (vm.exception())
return {};
hypot = Value(hypot.as_double() * hypot.as_double());
for (size_t i = 1; i < vm.argument_count(); ++i) {
auto cur = vm.argument(i).to_number(global_object);
if (vm.exception())
return {};
hypot = Value(hypot.as_double() + cur.as_double() * cur.as_double());
}
return Value(::sqrt(hypot.as_double()));
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::log)
{
auto number = vm.argument(0).to_number(global_object);
if (vm.exception())
return {};
if (number.as_double() < 0)
return js_nan();
return Value(::log(number.as_double()));
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::log2)
{
auto number = vm.argument(0).to_number(global_object);
if (vm.exception())
return {};
if (number.as_double() < 0)
return js_nan();
return Value(::log2(number.as_double()));
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::log10)
{
auto number = vm.argument(0).to_number(global_object);
if (vm.exception())
return {};
if (number.as_double() < 0)
return js_nan();
return Value(::log10(number.as_double()));
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::sinh)
{
auto number = vm.argument(0).to_number(global_object);
if (vm.exception())
return {};
if (number.is_nan())
return js_nan();
return Value(::sinh(number.as_double()));
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::cosh)
{
auto number = vm.argument(0).to_number(global_object);
if (vm.exception())
return {};
if (number.is_nan())
return js_nan();
return Value(::cosh(number.as_double()));
}
JS_DEFINE_NATIVE_FUNCTION(MathObject::tanh)
{
auto number = vm.argument(0).to_number(global_object);
if (vm.exception())
return {};
if (number.is_nan())
return js_nan();
if (number.is_positive_infinity())
return Value(1);
if (number.is_negative_infinity())
return Value(-1);
return Value(::tanh(number.as_double()));
}
}

View file

@ -0,0 +1,78 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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 MathObject final : public Object {
JS_OBJECT(MathObject, Object);
public:
explicit MathObject(GlobalObject&);
virtual void initialize(GlobalObject&) override;
virtual ~MathObject() override;
private:
JS_DECLARE_NATIVE_FUNCTION(abs);
JS_DECLARE_NATIVE_FUNCTION(random);
JS_DECLARE_NATIVE_FUNCTION(sqrt);
JS_DECLARE_NATIVE_FUNCTION(floor);
JS_DECLARE_NATIVE_FUNCTION(ceil);
JS_DECLARE_NATIVE_FUNCTION(round);
JS_DECLARE_NATIVE_FUNCTION(max);
JS_DECLARE_NATIVE_FUNCTION(min);
JS_DECLARE_NATIVE_FUNCTION(trunc);
JS_DECLARE_NATIVE_FUNCTION(sin);
JS_DECLARE_NATIVE_FUNCTION(cos);
JS_DECLARE_NATIVE_FUNCTION(tan);
JS_DECLARE_NATIVE_FUNCTION(pow);
JS_DECLARE_NATIVE_FUNCTION(exp);
JS_DECLARE_NATIVE_FUNCTION(expm1);
JS_DECLARE_NATIVE_FUNCTION(sign);
JS_DECLARE_NATIVE_FUNCTION(clz32);
JS_DECLARE_NATIVE_FUNCTION(acos);
JS_DECLARE_NATIVE_FUNCTION(acosh);
JS_DECLARE_NATIVE_FUNCTION(asin);
JS_DECLARE_NATIVE_FUNCTION(asinh);
JS_DECLARE_NATIVE_FUNCTION(atan);
JS_DECLARE_NATIVE_FUNCTION(atanh);
JS_DECLARE_NATIVE_FUNCTION(log1p);
JS_DECLARE_NATIVE_FUNCTION(cbrt);
JS_DECLARE_NATIVE_FUNCTION(atan2);
JS_DECLARE_NATIVE_FUNCTION(fround);
JS_DECLARE_NATIVE_FUNCTION(hypot);
JS_DECLARE_NATIVE_FUNCTION(log);
JS_DECLARE_NATIVE_FUNCTION(log2);
JS_DECLARE_NATIVE_FUNCTION(log10);
JS_DECLARE_NATIVE_FUNCTION(sinh);
JS_DECLARE_NATIVE_FUNCTION(cosh);
JS_DECLARE_NATIVE_FUNCTION(tanh);
};
}

View file

@ -0,0 +1,81 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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/Runtime/GlobalObject.h>
#include <LibJS/Runtime/LexicalEnvironment.h>
#include <LibJS/Runtime/NativeFunction.h>
#include <LibJS/Runtime/Value.h>
namespace JS {
NativeFunction* NativeFunction::create(GlobalObject& global_object, const FlyString& name, AK::Function<Value(VM&, GlobalObject&)> function)
{
return global_object.heap().allocate<NativeFunction>(global_object, name, move(function), *global_object.function_prototype());
}
NativeFunction::NativeFunction(Object& prototype)
: Function(prototype)
{
}
NativeFunction::NativeFunction(const FlyString& name, AK::Function<Value(VM&, GlobalObject&)> native_function, Object& prototype)
: Function(prototype)
, m_name(name)
, m_native_function(move(native_function))
{
}
NativeFunction::NativeFunction(const FlyString& name, Object& prototype)
: Function(prototype)
, m_name(name)
{
}
NativeFunction::~NativeFunction()
{
}
Value NativeFunction::call()
{
return m_native_function(vm(), global_object());
}
Value NativeFunction::construct(Function&)
{
return {};
}
LexicalEnvironment* NativeFunction::create_environment()
{
return heap().allocate<LexicalEnvironment>(global_object(), LexicalEnvironment::EnvironmentRecordType::Function);
}
bool NativeFunction::is_strict_mode() const
{
return vm().in_strict_mode();
}
}

Some files were not shown because too many files have changed in this diff Show more